feat(apk): scanner le QR du poste depuis l'app (ZXing.Delphi, Apache 2.0)

Le scanner du systeme demandait avec quoi ouvrir le lien. L'app ouvre la
camera arriere, decode une image sur quatre et remplit URL + jeton des
qu'un lien pmserver://sync est lu. Rien n'est enregistre avant Save.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-17 18:38:41 +01:00
parent 780eecc11b
commit dd9fd62dcb
81 changed files with 22141 additions and 19 deletions
@@ -0,0 +1,564 @@
{
* 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.
* Original Author: bbrown@google.com (Brian Brown)
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.BitMatrixParser;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.DecodeHintType,
ZXing.Common.BitMatrix,
ZXing.QrCode.Internal.FormatInformation,
ZXing.Common.Detector.MathUtils,
ZXing.Datamatrix.Internal.Version;
type
TBitMatrixParser = class sealed
private
mappingBitMatrix: TBitMatrix;
readMappingMatrix: TBitMatrix;
Fversion: TVersion;
/// <summary>
/// <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p>
///
/// <param name="row">Row to read in the mapping matrix</param>
/// <param name="column">Column to read in the mapping matrix</param>
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>value of the given bit in the mapping matrix</returns>
/// </summary>
function readModule(row, column, numRows, numColumns: Integer): Boolean;
/// <summary>
/// <p>Reads the 8 bits of the standard Utah-shaped pattern.</p>
///
/// <p>See ISO 16022:2006, 5.8.1 Figure 6</p>
///
/// <param name="row">Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern</param>
/// <param name="column">Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern</param>
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the utah shape</returns>
/// </summary>
function readUtah(row, column, numRows, numColumns: Integer): Integer;
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 1.</p>
///
/// <p>See ISO 16022:2006, Figure F.3</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 1</returns>
/// </summary>
function readCorner1(numRows, numColumns: Integer): Integer;
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 2.</p>
///
/// <p>See ISO 16022:2006, Figure F.4</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 2</returns>
/// </summary>
function readCorner2(numRows, numColumns: Integer): Integer;
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 3.</p>
///
/// <p>See ISO 16022:2006, Figure F.5</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 3</returns>
/// </summary>
function readCorner3(numRows, numColumns: Integer): Integer;
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 4.</p>
///
/// <p>See ISO 16022:2006, Figure F.6</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 4</returns>
/// </summary>
function readCorner4(numRows, numColumns: Integer): Integer;
/// <summary>
/// <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 name="bitMatrix">Original <see cref="BitMatrix" />including alignment patterns</param>
/// <returns><see cref="Version" />encapsulating the Data Matrix Code's "version"</returns>
/// <exception cref="FormatException">if the dimensions of the mapping matrix are not valid</exception>
/// Data Matrix dimensions.
/// </summary>
function readVersion(BitMatrix: TBitMatrix): TVersion;
public
/// <param name="bitMatrix">{@link TBitMatrix} to parse</param>
/// <throws>ReaderException if dimension is not >= 21 and 1 mod 4</throws>
constructor Create(const BitMatrix: TBitMatrix);
/// <summary>
/// <p>Extracts the data region from a <see cref="BitMatrix" />that contains
/// alignment patterns.</p>
///
/// <param name="bitMatrix">Original <see cref="BitMatrix" />with alignment patterns</param>
/// <returns>BitMatrix that has the alignment patterns removed</returns>
/// </summary>
function extractDataRegion(BitMatrix: TBitMatrix): TBitMatrix;
/// <summary>
/// <p>Reads the bits in the <see cref="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>
///
/// <returns>bytes encoded within the Data Matrix Code</returns>
/// <exception cref="FormatException">if the exact number of bytes expected is not read</exception>
/// </summary>
function readCodewords: TArray<Byte>;
destructor Destroy; override;
property Version: TVersion read Fversion;
end;
implementation
{ TBitMatrixParser }
constructor TBitMatrixParser.Create(const BitMatrix: TBitMatrix);
var
dimension: Integer;
begin
dimension := BitMatrix.Height;
if (((dimension >= 8) and (dimension <= 144)) and ((dimension and $01) = 0))
then
begin
self.Fversion := readVersion(BitMatrix);
if (self.Version <> nil) then
begin
self.mappingBitMatrix := extractDataRegion(BitMatrix);
self.readMappingMatrix := TBitMatrix.Create(self.mappingBitMatrix.Width,
self.mappingBitMatrix.Height)
end;
end;
end;
destructor TBitMatrixParser.Destroy;
begin
self.mappingBitMatrix.Free;
self.readMappingMatrix.Free;
inherited;
end;
function TBitMatrixParser.readVersion(BitMatrix: TBitMatrix): TVersion;
begin
Result := TVersion.getVersionForDimensions(BitMatrix.Height, BitMatrix.Width);
end;
function TBitMatrixParser.readCodewords: TArray<Byte>;
var
resultOffset, row, column, numRows, numColumns: Integer;
corner1Read, corner2Read, corner3Read, corner4Read: Boolean;
begin
Result := TArray<Byte>.Create();
SetLength(Result, Version.TotalCodewords);
resultOffset := 0;
row := 4;
column := 0;
numRows := self.mappingBitMatrix.Height;
numColumns := self.mappingBitMatrix.Width;
corner1Read := false;
corner2Read := false;
corner3Read := false;
corner4Read := false;
// Read all of the codewords
while ((row < numRows) or (column < numColumns)) do
begin
// Check the four corner cases
if ((row = numRows) and (column = 0) and (not corner1Read)) then
begin
Result[resultOffset] := Byte(readCorner1(numRows, numColumns));
Inc(resultOffset);
Dec(row, 2);
Inc(column, 2);
corner1Read := true;
end
else
begin
if ((row = (numRows - 2)) and (column = 0) and ((numColumns and $03) <> 0)
and (not corner2Read)) then
begin
Result[resultOffset] := Byte(readCorner2(numRows, numColumns));
Inc(resultOffset);
Dec(row, 2);
Inc(column, 2);
corner2Read := true;
end
else
begin
if ((row = (numRows + 4)) and (column = 2) and
((numColumns and $07) = 0) and (not corner3Read)) then
begin
Result[resultOffset] := Byte(readCorner3(numRows, numColumns));
Inc(resultOffset);
Dec(row, 2);
Inc(column, 2);
corner3Read := true;
end
else
begin
if ((row = (numRows - 2)) and (column = 0) and
((numColumns and $07) = 4) and (not corner4Read)) then
begin
Result[resultOffset] := Byte(readCorner4(numRows, numColumns));
Inc(resultOffset);
Dec(row, 2);
Inc(column, 2);
corner4Read := true;
end
else
begin
// Sweep upward diagonally to the right
while ((row >= 0) and (column < numColumns)) do
begin
if ((row < numRows) and (column >= 0) and
(not readMappingMatrix[column, row])) then
begin
Result[resultOffset] :=
Byte(readUtah(row, column, numRows, numColumns));
Inc(resultOffset);
end;
Dec(row, 2);
Inc(column, 2)
end;
Inc(row);
Inc(column, 3);
// Sweep downward diagonally to the left
while ((row < numRows) and (column >= 0)) do
begin
if ((row >= 0) and (column < numColumns) and
(not readMappingMatrix[column, row])) then
begin
Result[resultOffset] :=
Byte(readUtah(row, column, numRows, numColumns));
Inc(resultOffset);
end;
Inc(row, 2);
Dec(column, 2)
end;
Inc(row, 3);
Inc(column)
end;
end;
end;
end;
end;
if (resultOffset <> Version.TotalCodewords) then
Result := nil;
end;
function TBitMatrixParser.readModule(row, column, numRows,
numColumns: Integer): Boolean;
begin
// Adjust the row and column indices based on boundary wrapping
if (row < 0) then
begin
Inc(row, numRows);
Inc(column, (4 - ((numRows + 4) and $07)))
end;
if (column < 0) then
begin
Inc(column, numColumns);
Inc(row, (4 - ((numColumns + 4) and $07)))
end;
readMappingMatrix[column, row] := true;
Result := mappingBitMatrix[column, row];
end;
function TBitMatrixParser.readUtah(row, column, numRows,
numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((row - 2), (column - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((row - 2), (column - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((row - 1), (column - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((row - 1), (column - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((row - 1), column, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(row, (column - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(row, (column - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(row, column, numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.readCorner1(numRows, numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((numRows - 1), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), 1, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), 2, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(2, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(3, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.readCorner2(numRows, numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((numRows - 3), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 2), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 4), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 3), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.readCorner3(numRows, numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((numRows - 1), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 3), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 3), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.readCorner4(numRows, numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((numRows - 3), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 2), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (self.readModule(0, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(2, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(3, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.extractDataRegion(BitMatrix: TBitMatrix): TBitMatrix;
var
i, j, symbolSizeRows, dataRegionSizeRows,
dataRegionSizeColumns, numDataRegionsRow, numDataRegionsColumn,
sizeDataRegionRow, sizeDataRegionColumn, dataRegionRow, dataRegionRowOffset,
dataRegionColumn, dataRegionColumnOffset, readRowOffset, writeRowOffset,
readColumnOffset, writeColumnOffset: Integer;
bitMatrixWithoutAlignment: TBitMatrix;
begin
symbolSizeRows := Version.symbolSizeRows;
if (BitMatrix.Height <> symbolSizeRows) then
raise EArgumentException.Create
('Dimension of bitMarix must match the version size');
dataRegionSizeRows := Version.dataRegionSizeRows;
dataRegionSizeColumns := Version.dataRegionSizeColumns;
numDataRegionsRow := (symbolSizeRows div dataRegionSizeRows);
numDataRegionsColumn := (Version.symbolSizeColumns div dataRegionSizeColumns);
sizeDataRegionRow := (numDataRegionsRow * dataRegionSizeRows);
sizeDataRegionColumn := (numDataRegionsColumn * dataRegionSizeColumns);
bitMatrixWithoutAlignment := TBitMatrix.Create(sizeDataRegionColumn,
sizeDataRegionRow);
for dataRegionRow := 0 to Pred(numDataRegionsRow) do
begin
dataRegionRowOffset := (dataRegionRow * dataRegionSizeRows);
for dataRegionColumn := 0 to Pred(numDataRegionsColumn) do
begin
dataRegionColumnOffset := (dataRegionColumn * dataRegionSizeColumns);
for i := 0 to Pred(dataRegionSizeRows) do
begin
readRowOffset := (dataRegionRow * (dataRegionSizeRows + 2)) + 1 + i;
writeRowOffset := (dataRegionRowOffset + i);
for j := 0 to Pred(dataRegionSizeColumns) do
begin
readColumnOffset :=
(dataRegionColumn * (dataRegionSizeColumns + 2)) + 1 + j;
if (BitMatrix[readColumnOffset, readRowOffset]) then
begin
writeColumnOffset := (dataRegionColumnOffset + j);
bitMatrixWithoutAlignment[writeColumnOffset,
writeRowOffset] := true;
end;
end;
end;
end;
end;
Result := bitMatrixWithoutAlignment;
end;
end.
@@ -0,0 +1,179 @@
{
* 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.
* Original Author: bbrown@google.com (Brian Brown)
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.DataBlock;
interface
uses
System.SysUtils,
ZXing.Datamatrix.Internal.Version;
type
/// <summary>
/// <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>
/// </summary>
TDataBlock = class sealed
private
Fcodewords: TArray<Byte>;
FNumDataCodewords: Integer;
public
constructor Create(const Acodewords: TArray<Byte>;
const ANumDataCodewords: Integer);
destructor Destroy;override;
class function getDataBlocks(rawCodewords: TArray<Byte>;
version: TVersion): TArray<TDataBlock>;
property codewords: TArray<Byte> read Fcodewords;
property NumDataCodewords: Integer read FNumDataCodewords;
end;
implementation
{ TDataBlock }
constructor TDataBlock.Create(const Acodewords: TArray<Byte>;
const ANumDataCodewords: Integer);
begin
FnumDataCodewords := AnumDataCodewords;
Fcodewords := Acodewords;
end;
destructor TDataBlock.Destroy;
begin
Fcodewords := nil;
inherited;
end;
class function TDataBlock.getDataBlocks(rawCodewords: TArray<Byte>;
version: TVersion): TArray<TDataBlock>;
var
i, j: Integer;
ecBlock : TVersion.TECB;
ecBlocks : TVersion.TECBlocks;
totalBlocks : Integer;
ecBlockArray : TArray<TVersion.TECB>;
byteArray : TArray<Byte>;
numResultBlocks,
numDataCodewords,
numBlockCodewords,
longerBlocksTotalCodewords,
//shorterBlocksTotalCodewords
longerBlocksNumDataCodewords,
shorterBlocksNumDataCodewords,
rawCodewordsOffset,
numLongerBlocks,
max,
iOffset,
jOffset : Integer;
specialVersion: Boolean;
begin
// Figure out the number and size of data blocks used by this version
ecBlocks := version.ecBlocks;
// First count the total number of data blocks
totalBlocks := 0;
ecBlockArray := ecBlocks.ECBBlocksValue;
for ecBlock in ecBlockArray do
begin
inc(totalBlocks, ecBlock.Count)
end;
// Now establish DataBlocks of the appropriate size and number of data codewords
result := TArray<TDataBlock>.Create();
SetLength(result, totalBlocks);
numResultBlocks := 0;
for ecBlock in ecBlockArray do
begin
for i := 0 to Pred(ecBlock.Count) do
begin
numDataCodewords := ecBlock.DataCodewords;
numBlockCodewords := (ecBlocks.ECCodewords + numDataCodewords);
byteArray := TArray<Byte>.Create();
SetLength(byteArray, numBlockCodewords);
result[numResultBlocks] := TDataBlock.Create(byteArray, numDataCodewords);
Inc(numResultBlocks);
end
end;
// 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: There is only one case where there is a difference for Data Matrix for size 144
longerBlocksTotalCodewords := Length(result[0].codewords);
//shorterBlocksTotalCodewords := (longerBlocksTotalCodewords - 1);
longerBlocksNumDataCodewords := (longerBlocksTotalCodewords - ecBlocks.ECCodewords);
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
rawCodewordsOffset := 0;
for i := 0 to Pred(shorterBlocksNumDataCodewords) do
begin
for j := 0 to Pred(numResultBlocks) do
begin
result[j].codewords[i] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
end;
// Fill out the last data block in the longer ones
specialVersion := (version.versionNumber = 24);
if specialVersion
then
numLongerBlocks := 8
else
numLongerBlocks := numResultBlocks;
for j := 0 to Pred(numLongerBlocks) do
begin
result[j].codewords[(longerBlocksNumDataCodewords - 1)] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
// Now add in error correction blocks
max := Length(result[0].codewords);
for i := longerBlocksNumDataCodewords to Pred(max) do
begin
for j := 0 to Pred(numResultBlocks) do
begin
if (specialVersion)
then
jOffset := (j + 8) mod numResultBlocks
else
jOffset := j;
if (specialVersion and (jOffset > 7))
then
iOffset := (i - 1)
else
iOffset := i;
result[jOffset].codewords[iOffset] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
end;
if (not (rawCodewordsOffset = Length(rawCodewords)))
then
raise EArgumentException.Create('Arguments error');
end;
end.
@@ -0,0 +1,903 @@
{
* 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.
* Original Authors: bbrown@google.com (Brian Brown) and Sean Owen
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.DecodedBitStreamParser;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.DecoderResult,
ZXing.ByteSegments,
ZXing.BitSource;
type
/// <summary>
/// <p>Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes
/// in one Data Matrix Code. This class decodes the bits back into text.</p>
///
/// <p>See ISO 16022:2006, 5.2.1 - 5.2.9.2</p>
/// </summary>
TDecodedBitStreamParser = class abstract
type
TMode = (PAD_ENCODE, // Not really a mode
ASCII_ENCODE, C40_ENCODE, TEXT_ENCODE, ANSIX12_ENCODE, EDIFACT_ENCODE,
BASE256_ENCODE);
private
class var C40_BASIC_SET_CHARS, C40_SHIFT2_SET_CHARS, TEXT_BASIC_SET_CHARS,
TEXT_SHIFT2_SET_CHARS, TEXT_SHIFT3_SET_CHARS: TArray<Char>;
class procedure InitializeClass;
class procedure FinalizeClass;
class function decodeC40Segment(bits: TBitSource;
res: TStringBuilder): boolean;
class function decodeTextSegment(bits: TBitSource;
res: TStringBuilder): boolean;
class function decodeAnsiX12Segment(bits: TBitSource;
res: TStringBuilder): boolean;
class function decodeEdifactSegment(bits: TBitSource;
res: TStringBuilder): boolean;
class function decodeBase256Segment(bits: TBitSource; res: TStringBuilder;
byteSegments:IByteSegments ): boolean;
class function decodeAsciiSegment(bits: TBitSource; res: TStringBuilder;
resultTrailer: TStringBuilder; var mode: TMode): boolean;
class procedure parseTwoBytes(firstByte: Integer; secondByte: Integer;
result: TArray<Integer>);
class function unrandomize255State(randomizedBase256Codeword: Integer;
base256CodewordPosition: Integer): Integer;
public
class function decode(bytes: TArray<Byte>): TDecoderResult;
end;
implementation
{ TDecodedBitStreamParser }
class function TDecodedBitStreamParser.decode(bytes: TArray<Byte>)
: TDecoderResult;
var
bits: TBitSource;
res, resultTrailer: TStringBuilder;
byteSegments: IByteSegments;
mode: TMode;
begin
bits := TBitSource.Create(bytes);
res := TStringBuilder.Create(100);
resultTrailer := TStringBuilder.Create(0);
byteSegments := ByteSegmentsCreate;
try
byteSegments.Add(TArray<Byte>.Create()); // TODO (KG): Validation
mode := TMode.ASCII_ENCODE;
while ((mode <> TMode.PAD_ENCODE) and (bits.available() > 0)) do
begin
if (mode = TMode.ASCII_ENCODE) then
begin
if (not TDecodedBitStreamParser.decodeAsciiSegment(bits, res,
resultTrailer, mode)) then
begin
result := nil; // this line was totally missing. I assume the correct thing is to set result = nil.
exit;
end;
end
else
begin
case mode of
TMode.C40_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeC40Segment(bits, res)) then
begin
result := nil;
exit;
end;
end;
TMode.TEXT_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeTextSegment(bits, res)) then
begin
result := nil;
exit;
end;
end;
TMode.ANSIX12_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeAnsiX12Segment(bits, res))
then
begin
result := nil;
exit;
end;
end;
TMode.EDIFACT_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeEdifactSegment(bits, res))
then
begin
result := nil;
exit;
end;
end;
TMode.BASE256_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeBase256Segment(bits, res,
byteSegments)) then
begin
result := nil;
exit;
end;
end;
else
begin
result := nil;
exit;
end;
end;
mode := TMode.ASCII_ENCODE;
end;
end;
if (resultTrailer.Length > 0) then
res.Append(resultTrailer.ToString);
if (byteSegments.Count = 0) then
result := TDecoderResult.Create(bytes, res.ToString, nil, '')
else
result := TDecoderResult.Create(bytes, res.ToString, byteSegments, '');
finally
bits.Free;
res.Free;
resultTrailer.Free;
//
// for i := 0 to byteSegments.Count - 1 do
// begin
//
// end;
//
// byteSegments.Free;
end;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.7
/// </summary>
class function TDecodedBitStreamParser.decodeAnsiX12Segment(bits: TBitSource;
res: TStringBuilder): boolean;
var
i: Integer;
cValues: TArray<Integer>;
firstByte: Integer;
cValue: Integer;
begin
// Three ANSI X12 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
cValues := TArray<Integer>.Create(0, 0, 0);
while (bits.available() > 0) do
begin
// If there is only one byte left then it will be encoded as ASCII
if (bits.available = 8) then
begin
result := true;
exit;
end;
firstByte := bits.readBits(8);
if (firstByte = 254) then
begin
// Unlatch codeword
result := true;
exit;
end;
TDecodedBitStreamParser.parseTwoBytes(firstByte, bits.readBits(8), cValues);
for i := 0 to 2 do
begin
cValue := cValues[i];
case cValue of
0:
begin
// X12 segment terminator <CR>
res.Append(#13);
end;
1:
begin
// X12 segment separator *
res.Append('*');
end;
2:
begin
// X12 sub-element separator >
res.Append('>');
end;
3:
begin
// space
res.Append(' ');
end;
else
begin
if (cValue < 14) then
// 0 - 9
res.Append(Char(cValue + 44))
else if (cValue < 40) then
// A - Z
res.Append(Char(cValue + 51))
else
begin
result := false;
exit;
end;
end;
end;
end;
end;;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
/// </summary>
class function TDecodedBitStreamParser.decodeAsciiSegment(bits: TBitSource;
res: TStringBuilder; resultTrailer: TStringBuilder; var mode: TMode): boolean;
var
oneByte: Integer;
upperShift: boolean;
value: Byte;
begin
upperShift := false;
mode := TMode.ASCII_ENCODE;
while (bits.available() > 0) do
begin
oneByte := bits.readBits(8);
if (oneByte = 0) then
begin
result := false;
exit;
end
else
begin
if (oneByte <= 128) then
begin
// ASCII data (ASCII value + 1)
if (upperShift) then
begin
Inc(oneByte, 128);
// upperShift := false;
end;
res.Append(Char(oneByte - 1));
mode := TMode.ASCII_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 129) then
begin
// Pad
mode := TMode.PAD_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte <= 229) then
begin
// 2-digit data 00-99 (Numeric Value + 130)
value := (oneByte - 130);
if (value < 10) then
// pad with '0' for single digit values
res.Append('0');
res.Append(value);
end
else
begin
if (oneByte = 230) then
begin
// Latch to C40 encodation
mode := TMode.C40_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 231) then
begin
// Latch to Base 256 encodation
mode := TMode.BASE256_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 232) then
begin
// FNC1
res.Append(Char(29)); // translate as ASCII 29
end
else
begin
if ((oneByte = 233) or (oneByte = 234)) then
begin
// Structured Append, Reader Programming
// Ignore these symbols for now
// throw EReaderException.Instance;
end
else
begin
if (oneByte = 235) then
begin
// Upper Shift (shift to Extended ASCII)
upperShift := true;
end
else
begin
if (oneByte = 236) then
begin
// 05 Macro
res.Append('[)>' + #$001E05 + #$001D);
res.Insert(0, #$001E + #$0004);
end
else
begin
if (oneByte = 237) then
begin
// 06 Macro
res.Append('[)>' + #$001E06 + #$001D);
res.Insert(0, #$001E + #$0004);
end
else
begin
if (oneByte = 238) then
begin
// Latch to ANSI X12 encodation
mode := TMode.ANSIX12_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 239) then
begin
// Latch to Text encodation
mode := TMode.TEXT_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 240) then
begin
mode := TMode.EDIFACT_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 241) then
begin
// ECI Character
// TODO: I think we need to support ECI
// throw TReaderException.Instance;
// Ignore this symbol for now
end
else if (oneByte >= 242) then
begin
// Not to be used in ASCII encodation
// ... but work around encoders that end with 254, latch back to ASCII
if ((oneByte <> 254) or (bits.available() <> 0)) then
begin
result := false;
exit;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
mode := TMode.ASCII_ENCODE;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.9 and Annex B, B.2
/// </summary>
class function TDecodedBitStreamParser.decodeBase256Segment(bits: TBitSource;
res: TStringBuilder; byteSegments: IByteSegments ): boolean;
var
i, Count, codewordPosition, d1: Integer;
bytes: TArray<Byte>;
begin
// Figure out how long the Base 256 Segment is.
codewordPosition := (1 + bits.ByteOffset); // position is 1-indexed
d1 := TDecodedBitStreamParser.unrandomize255State(bits.readBits(8),
codewordPosition);
Inc(codewordPosition);
if (d1 = 0) then
// Read the remainder of the symbol
Count := (bits.available div 8)
else if (d1 < 250) then
Count := d1
else
begin
Count := ((250 * (d1 - 249)) + TDecodedBitStreamParser.unrandomize255State
(bits.readBits(8), codewordPosition));
Inc(codewordPosition);
end;
// We're seeing NegativeArraySizeException errors from users.
if (Count < 0) then
begin
result := false;
exit;
end;
bytes := TArray<Byte>.Create();
SetLength(bytes, Count);
for i := 0 to Pred(Count) do
begin
// Have seen this particular error in the wild, such as at
// http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2
if (bits.available() < 8) then
begin
result := false;
exit;
end;
bytes[i] := Byte(TDecodedBitStreamParser.unrandomize255State
(bits.readBits(8), codewordPosition));
Inc(codewordPosition);
end;
byteSegments.Add(bytes);
try
res.Append(TEncoding.GetEncoding('ISO-8859-1').GetString(bytes))
except
on uee: Exception do
raise EInvalidOpException.Create
('Platform does not support required encoding: ' + uee.Message)
end;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
/// </summary>
class function TDecodedBitStreamParser.decodeC40Segment(bits: TBitSource;
res: TStringBuilder): boolean;
var
i: Integer;
c40char: Char;
cValues: TArray<Integer>;
upperShift: boolean;
shift: Integer;
firstByte: Byte;
cValue: Integer;
begin
// Three C40 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO: The Upper Shift with C40 doesn't work in the 4 value scenario all the time
upperShift := false;
cValues := TArray<Integer>.Create(0, 0, 0);
shift := 0;
while (bits.available() > 0) do
begin
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() = 8) then
begin
result := true;
exit;
end;
firstByte := bits.readBits(8);
if (firstByte = 254) then
begin
// Unlatch codeword
result := true;
exit;
end;
TDecodedBitStreamParser.parseTwoBytes(firstByte, bits.readBits(8), cValues);
for i := 0 to 2 do
begin
cValue := cValues[i];
case shift of
0:
begin
if (cValue < 3) then
shift := (cValue + 1)
else
begin
if (cValue < Length(C40_BASIC_SET_CHARS)) then
begin
c40char := C40_BASIC_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(c40char) + Char(128));
upperShift := false;
end
else
res.Append(c40char);
end
else
begin
result := false;
exit;
end;
end;
end;
1:
begin
if (upperShift) then
begin
res.Append(Char(cValue) + Char(128));
upperShift := false;
end
else
res.Append(Char(cValue));
shift := 0;
end;
2:
begin
if (cValue < Length(C40_SHIFT2_SET_CHARS)) then
begin
c40char := C40_SHIFT2_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(c40char) + Char(128));
upperShift := false;
end
else
res.Append(c40char);
end
else
begin
if (cValue = 27) then
// FNC1
res.Append(Char(29)) // translate as ASCII 29
else if (cValue = 30) then
// Upper Shift
upperShift := true
else
begin
result := false;
exit;
end;
end;
shift := 0;
end;
3:
begin
if (upperShift) then
begin
res.Append(Char(cValue + 254));
upperShift := false;
end
else
res.Append(Char(cValue + 96));
shift := 0;
end;
else
begin
result := false;
exit;
end;
end;
end;
end;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.8 and Annex C Table C.3
/// </summary>
class function TDecodedBitStreamParser.decodeEdifactSegment(bits: TBitSource;
res: TStringBuilder): boolean;
var
i: Integer;
edifactValue: Integer;
bitsLeft: Integer;
begin
while (bits.available() > 0) do
begin
// If there is only two or less bytes left then it will be encoded as ASCII
if (bits.available() <= 16) then
begin
result := true;
exit;
end;
for i := 0 to 3 do
begin
edifactValue := bits.readBits(6);
// Check for the unlatch character
if (edifactValue = $1F) then
begin
// 011111
// Read rest of byte, which should be 0, and stop
bitsLeft := (8 - bits.BitOffset);
if (not bitsLeft = 8) then
bits.readBits(bitsLeft);
result := true;
exit;
end;
if ((edifactValue and $20) = 0) then
begin
// no 1 in the leading (6th) bit
edifactValue := (edifactValue or $40);
// Add a leading 01 to the 6 bit binary value
end;
res.Append(Char(edifactValue));
end;
end;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
/// </summary>
class function TDecodedBitStreamParser.decodeTextSegment(bits: TBitSource;
res: TStringBuilder): boolean;
var
textChar: Char;
cValues: TArray<Integer>;
upperShift: boolean;
i, shift: Integer;
firstByte: Integer;
cValue: Integer;
begin
// Three Text values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO: The Upper Shift with Text doesn't work in the 4 value scenario all the time
upperShift := false;
cValues := TArray<Integer>.Create(0, 0, 0);
shift := 0;
while (bits.available() > 0) do
begin
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() = 8) then
begin
result := true;
exit;
end;
firstByte := bits.readBits(8);
if (firstByte = 254) then
begin
// Unlatch codeword
result := true;
exit;
end;
TDecodedBitStreamParser.parseTwoBytes(firstByte, bits.readBits(8), cValues);
for i := 0 to 2 do
begin
cValue := cValues[i];
case (shift) of
0:
begin
if (cValue < 3) then
shift := (cValue + 1)
else
begin
if (cValue < Length(TEXT_BASIC_SET_CHARS)) then
begin
textChar := TEXT_BASIC_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(textChar) + Char(128));
upperShift := false;
end
else
res.Append(textChar);
end
else
begin
result := false;
exit;
end;
end;
end;
1:
begin
if (upperShift) then
begin
res.Append(Char(cValue + 128));
upperShift := false;
end
else
res.Append(Char(cValue));
shift := 0;
end;
2:
begin
// Shift 2 for Text is the same encoding as C40
if (cValue < Length(TEXT_SHIFT2_SET_CHARS)) then
begin
textChar := TEXT_SHIFT2_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(textChar) + Char(128));
upperShift := false;
end
else
res.Append(textChar);
end
else
begin
if (cValue = 27) then
// FNC1
res.Append(Char(29)) // translate as ASCII 29
else if (cValue = 30) then
// Upper Shift
upperShift := true
else
begin
result := false;
exit;
end;
end;
shift := 0;
end;
3:
begin
if (cValue < Length(TEXT_SHIFT3_SET_CHARS)) then
begin
textChar := TEXT_SHIFT3_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(textChar) + Char(128));
upperShift := false;
end
else
res.Append(textChar);
shift := 0;
end
else
begin
result := false;
exit;
end;
end;
else
begin
result := false;
exit;
end;
end;
end;
end;
result := true;
end;
class procedure TDecodedBitStreamParser.parseTwoBytes(firstByte: Integer;
secondByte: Integer; result: TArray<Integer>);
var
fullBitValue, temp: Integer;
begin
fullBitValue := (((firstByte shl 8) + secondByte) - 1);
temp := (fullBitValue div $640);
result[0] := temp;
dec(fullBitValue, (temp * $640));
temp := (fullBitValue div 40);
result[1] := temp;
result[2] := (fullBitValue - (temp * 40))
end;
/// <summary>
/// See ISO 16022:2006, Annex B, B.2
/// </summary>
class function TDecodedBitStreamParser.unrandomize255State
(randomizedBase256Codeword: Integer;
base256CodewordPosition: Integer): Integer;
var
pseudoRandomNumber: Integer;
tempVariable: Integer;
begin
pseudoRandomNumber := (((149 * base256CodewordPosition) mod 255) + 1);
tempVariable := (randomizedBase256Codeword - pseudoRandomNumber);
if (tempVariable >= 0) then
result := tempVariable
else
result := (tempVariable + 256);
end;
class procedure TDecodedBitStreamParser.InitializeClass;
begin
/// <summary>
/// See ISO 16022:2006, Annex C Table C.1
/// The C40 Basic Character Set (*'s used for placeholders for the shift values)
/// </summary>
C40_BASIC_SET_CHARS := TArray<Char>.Create('*', '*', '*', ' ', '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');
C40_SHIFT2_SET_CHARS := TArray<Char>.Create('!', '"', '#', '$', '%', '&',
'''', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
'@', '[', '\', ']', '^', '_');
/// <summary>
/// See ISO 16022:2006, Annex C Table C.2
/// The Text Basic Character Set (*'s used for placeholders for the shift values)
/// </summary>
TEXT_BASIC_SET_CHARS := TArray<Char>.Create('*', '*', '*', ' ', '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');
// Shift 2 for Text is the same encoding as C40
TEXT_SHIFT2_SET_CHARS := C40_SHIFT2_SET_CHARS;
TEXT_SHIFT3_SET_CHARS := TArray<Char>.Create('`', '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', '{', '|', '}', '~', '');
end;
class procedure TDecodedBitStreamParser.FinalizeClass;
begin
C40_BASIC_SET_CHARS := nil;
C40_SHIFT2_SET_CHARS := nil;
TEXT_BASIC_SET_CHARS := nil;
TEXT_SHIFT2_SET_CHARS := nil;
TEXT_SHIFT3_SET_CHARS := nil;
end;
initialization
TDecodedBitStreamParser.InitializeClass;
finalization
TDecodedBitStreamParser.FinalizeClass;
end.
@@ -0,0 +1,229 @@
{
* 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.
* Original Author: bbrown@google.com (Brian Brown)
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.Decoder;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.DecodeHintType,
ZXing.Common.BitMatrix,
ZXing.Datamatrix.Internal.BitMatrixParser,
ZXing.Datamatrix.Internal.DataBlock,
ZXing.Common.ReedSolomon.ReedSolomonDecoder,
ZXing.Common.ReedSolomon.GenericGF,
ZXing.DecoderResult,
ZXing.Datamatrix.Internal.DecodedBitStreamParser;
type
/// <summary>
/// <p>The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting
/// the Data Matrix Code from an image.</p>
/// </summary>
TDataMatrixDecoder = class sealed
private
rsDecoder: TReedSolomonDecoder;
public
constructor Create;
destructor Destroy; override;
function correctErrors(codewordBytes: TArray<Byte>; numDataCodewords: Integer): boolean;
function decode(bits: TBitMatrix): TDecoderResult; overload;
function decode(image: TArray < TArray < boolean >> ): TDecoderResult; overload;
end;
implementation
{ TDataMatrixDecoder }
/// <summary>
/// Initializes a new instance of the <see cref="Decoder"/> class.
/// </summary>
constructor TDataMatrixDecoder.Create;
begin
rsDecoder := TReedSolomonDecoder.Create(TGenericGF.DATA_MATRIX_FIELD_256);
end;
destructor TDataMatrixDecoder.Destroy;
begin
FreeAndNil(rsDecoder);
inherited;
end;
/// <summary>
/// <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 name="image">booleans representing white/black Data Matrix Code modules</param>
/// <returns>text and bytes encoded within the Data Matrix Code</returns>
/// <exception cref="FormatException">if the Data Matrix Code cannot be decoded</exception>
/// <exception cref="ChecksumException">if error correction fails</exception>
/// </summary>
function TDataMatrixDecoder.decode(image: TArray < TArray < boolean >> ): TDecoderResult;
var
i, j: Integer;
dimension: Integer;
bits: TBitMatrix;
begin
dimension := Length(image);
bits := TBitMatrix.Create(dimension);
for i := 0 to Pred(dimension) do
begin
for j := 0 to Pred(dimension) do
begin
if (image[i][j]) then
bits[j, i] := true;
end;
end;
Result := decode(bits);
end;
/// <summary>
/// <p>Decodes a Data Matrix Code represented as a <see cref="BitMatrix" />. A 1 or "true" is taken
/// to mean a black module.</p>
/// </summary>
/// <param name="bits">booleans representing white/black Data Matrix Code modules</param>
/// <returns>text and bytes encoded within the Data Matrix Code</returns>
function TDataMatrixDecoder.decode(bits: TBitMatrix): TDecoderResult;
var
i, j: Integer;
db: TDataBlock;
parser: TBitMatrixParser;
codewords: TArray<Byte>;
dataBlocks: TArray<TDataBlock>;
dataBlocksCount, totalBytes: Integer;
resultBytes: TArray<Byte>;
DataBlock: TDataBlock;
codewordBytes: TArray<Byte>;
numDataCodewords: Integer;
begin
// Construct a parser and read version, error-correction level
parser := TBitMatrixParser.Create(bits);
DataBlock := nil;
try
if (parser.Version = nil) then
begin
Result := nil;
exit;
end;
// Read codewords
codewords := parser.readCodewords;
if (codewords = nil) then
begin
Result := nil;
exit;
end;
// Separate into data blocks
dataBlocks := TDataBlock.getDataBlocks(codewords, parser.Version);
dataBlocksCount := Length(dataBlocks);
// Count total number of data bytes
totalBytes := 0;
for db in dataBlocks do
begin
Inc(totalBytes, db.numDataCodewords)
end;
resultBytes := TArray<Byte>.Create();
SetLength(resultBytes, totalBytes);
// Error-correct and copy data blocks together into a stream of bytes
for j := 0 to Pred(dataBlocksCount) do
begin
DataBlock := dataBlocks[j];
codewordBytes := DataBlock.codewords;
numDataCodewords := DataBlock.numDataCodewords;
if (not correctErrors(codewordBytes, numDataCodewords)) then
begin
if Assigned(DataBlock) then
DataBlock.Free;
DataBlock := nil;
resultBytes := nil;
codewordBytes := nil;
Result := nil;
exit;
end;
for i := 0 to Pred(numDataCodewords) do
begin
// De-interlace data blocks.
resultBytes[(i * dataBlocksCount) + j] := codewordBytes[i];
end;
DataBlock.Free;
DataBlock := nil;
end;
// Decode the contents of that stream of bytes
Result := TDecodedBitStreamParser.decode(resultBytes);
finally
if Assigned(DataBlock) then
DataBlock.Free;
resultBytes := nil;
codewordBytes := nil;
parser.Free;
end;
end;
/// <summary>
/// <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 name="codewordBytes">data and error correction codewords</param>
/// <param name="numDataCodewords">number of codewords that are data bytes</param>
/// </summary>
function TDataMatrixDecoder.correctErrors(codewordBytes: TArray<Byte>; numDataCodewords: Integer): boolean;
var
i, numCodewords, numECCodewords: Integer;
codewordsInts: TArray<Integer>;
begin
numCodewords := Length(codewordBytes);
// First read into an array of ints
codewordsInts := TArray<Integer>.Create();
SetLength(codewordsInts, numCodewords);
for i := 0 to Pred(numCodewords) do
begin
codewordsInts[i] := (codewordBytes[i] and $FF);
end;
numECCodewords := (Length(codewordBytes) - numDataCodewords);
if (not rsDecoder.decode(codewordsInts, numECCodewords)) then
begin
Result := false;
exit;
end;
// 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 i := 0 to Pred(numDataCodewords) do
begin
codewordBytes[i] := Byte(codewordsInts[i]);
end;
Result := true;
end;
end.
@@ -0,0 +1,290 @@
{
* 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.
* Original Author: bbrown@google.com (Brian Brown)
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.Version;
interface
uses
System.SysUtils,
ZXing.Common.BitMatrix,
ZXing.QrCode.Internal.FormatInformation,
ZXing.Common.Detector.MathUtils;
type
/// <summary>
/// The Version object encapsulates attributes about a particular
/// size Data Matrix Code.
/// </summary>
TVersion = class sealed
type
/// <summary>
/// <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>
/// </summary>
TECB = class
private
Fcount: Integer;
FdataCodewords: Integer;
public
constructor Create(count: Integer; dataCodewords: Integer);
property count: Integer read Fcount;
property dataCodewords: Integer read FdataCodewords;
end;
/// <summary>
/// <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>
/// </summary>
TECBlocks = class
private
_ecBlocksValue: TArray<TECB>;
_ecCodewords: Integer;
public
property ECBBlocksValue: TArray<TECB> read _ecBlocksValue;
property ECCodewords: Integer read _ecCodewords;
constructor Create(ECCodewords: Integer; ecBlocks: TECB); overload;
constructor Create(ECCodewords: Integer; ecBlocks1: TECB;
ecBlocks2: TECB); overload;
destructor Destroy; override;
end;
private
FdataRegionSizeColumns: Integer;
FdataRegionSizeRows: Integer;
FecBlocks: TECBlocks;
FsymbolSizeColumns: Integer;
FsymbolSizeRows: Integer;
FtotalCodewords: Integer;
FversionNumber: Integer;
class var buildVersions: TArray<TVersion>;
class procedure ClassInit; static;
class procedure ClassFinal; static;
class function GetBuildVersions: TArray<TVersion>; static;
constructor Create(versionNumber, symbolSizeRows, symbolSizeColumns,
dataRegionSizeRows, dataRegionSizeColumns: Integer; ecBlocks: TECBlocks);
public
destructor Destroy; override;
function ToString: string; override;
class function getVersionForDimensions(numRows: Integer;
numColumns: Integer): TVersion;
property dataRegionSizeColumns: Integer read FdataRegionSizeColumns;
property dataRegionSizeRows: Integer read FdataRegionSizeRows;
property ecBlocks: TECBlocks read FecBlocks;
property symbolSizeColumns: Integer read FsymbolSizeColumns;
property symbolSizeRows: Integer read FsymbolSizeRows;
property totalCodewords: Integer read FtotalCodewords;
property versionNumber: Integer read FversionNumber;
end;
implementation
{ TVersion.TECB }
constructor TVersion.TECB.Create(count, dataCodewords: Integer);
begin
self.Fcount := count;
self.FdataCodewords := dataCodewords;
end;
{ TVersion.TECBlocks }
constructor TVersion.TECBlocks.Create(ECCodewords: Integer; ecBlocks: TECB);
begin
self._ecCodewords := ECCodewords;
self._ecBlocksValue := TArray<TECB>.Create(ecBlocks);
end;
constructor TVersion.TECBlocks.Create(ECCodewords: Integer; ecBlocks1: TECB;
ecBlocks2: TECB);
begin
self._ecCodewords := ECCodewords;
self._ecBlocksValue := TArray<TECB>.Create(ecBlocks1, ecBlocks2);
end;
destructor TVersion.TECBlocks.Destroy;
var
ecb: TECB;
i: Integer;
begin
for i := 0 to Length(_ecBlocksValue) - 1 do
begin
ecb := _ecBlocksValue[i];
FreeAndNil(ecb);
end;
_ecBlocksValue := nil;
inherited;
end;
{ TVersion }
class procedure TVersion.ClassInit();
begin
TVersion.buildVersions := TVersion.GetBuildVersions;
end;
class procedure TVersion.ClassFinal();
var
Version: TVersion;
begin
for Version in TVersion.buildVersions do
begin
Version.Free;
end;
TVersion.buildVersions := nil;
end;
constructor TVersion.Create(versionNumber, symbolSizeRows, symbolSizeColumns,
dataRegionSizeRows, dataRegionSizeColumns: Integer; ecBlocks: TECBlocks);
var
ecBlock: TECB;
total: Integer;
ECCodewords: Integer;
begin
self.FversionNumber := versionNumber;
self.FsymbolSizeRows := symbolSizeRows;
self.FsymbolSizeColumns := symbolSizeColumns;
self.FdataRegionSizeRows := dataRegionSizeRows;
self.FdataRegionSizeColumns := dataRegionSizeColumns;
self.FecBlocks := ecBlocks;
total := 0;
ECCodewords := ecBlocks.ECCodewords;
for ecBlock in ecBlocks.ECBBlocksValue do
begin
Inc(total, (ecBlock.count * (ecBlock.dataCodewords + ECCodewords)))
end;
self.FtotalCodewords := total;
end;
destructor TVersion.Destroy;
begin
FreeAndNil(FecBlocks);
inherited;
end;
/// <summary>
/// See ISO 16022:2006 5.5.1 Table 7
/// </summary>
class function TVersion.GetBuildVersions: TArray<TVersion>;
begin
Result := TArray<TVersion>.Create(TVersion.Create(1, 10, 10, 8, 8,
TECBlocks.Create(5, TECB.Create(1, 3))), TVersion.Create(2, 12, 12, 10, 10,
TECBlocks.Create(7, TECB.Create(1, 5))), TVersion.Create(3, 14, 14, 12, 12,
TECBlocks.Create(10, TECB.Create(1, 8))), TVersion.Create(4, $10, $10, 14,
14, TECBlocks.Create(12, TECB.Create(1, 12))), TVersion.Create(5, $12, $12,
$10, $10, TECBlocks.Create(14, TECB.Create(1, $12))),
TVersion.Create(6, 20, 20, $12, $12, TECBlocks.Create($12,
TECB.Create(1, $16))), TVersion.Create(7, $16, $16, 20, 20,
TECBlocks.Create(20, TECB.Create(1, 30))), TVersion.Create(8, $18, $18, $16,
$16, TECBlocks.Create($18, TECB.Create(1, $24))),
TVersion.Create(9, $1A, $1A, $18, $18, TECBlocks.Create($1C,
TECB.Create(1, $2C))), TVersion.Create(10, $20, $20, 14, 14,
TECBlocks.Create($24, TECB.Create(1, $3E))), TVersion.Create(11, $24, $24,
$10, $10, TECBlocks.Create($2A, TECB.Create(1, $56))),
TVersion.Create(12, 40, 40, $12, $12, TECBlocks.Create($30,
TECB.Create(1, $72))), TVersion.Create(13, $2C, $2C, 20, 20,
TECBlocks.Create($38, TECB.Create(1, $90))), TVersion.Create(14, $30, $30,
$16, $16, TECBlocks.Create($44, TECB.Create(1, $AE))),
TVersion.Create(15, $34, $34, $18, $18, TECBlocks.Create($2A,
TECB.Create(2, $66))), TVersion.Create($10, $40, $40, 14, 14,
TECBlocks.Create($38, TECB.Create(2, 140))), TVersion.Create($11, $48, $48,
$10, $10, TECBlocks.Create($24, TECB.Create(4, $5C))),
TVersion.Create($12, 80, 80, $12, $12, TECBlocks.Create($30,
TECB.Create(4, $72))), TVersion.Create($13, $58, $58, 20, 20,
TECBlocks.Create($38, TECB.Create(4, $90))), TVersion.Create(20, $60, $60,
$16, $16, TECBlocks.Create($44, TECB.Create(4, $AE))),
TVersion.Create($15, $68, $68, $18, $18, TECBlocks.Create($38,
TECB.Create(6, $88))), TVersion.Create($16, 120, 120, $12, $12,
TECBlocks.Create($44, TECB.Create(6, $AF))), TVersion.Create($17, $84, $84,
20, 20, TECBlocks.Create($3E, TECB.Create(8, $A3))),
TVersion.Create($18, $90, $90, $16, $16, TECBlocks.Create($3E,
TECB.Create(8, $9C), TECB.Create(2, $9B))), TVersion.Create($19, 8, $12, 6,
$10, TECBlocks.Create(7, TECB.Create(1, 5))), TVersion.Create($1A, 8, $20,
6, 14, TECBlocks.Create(11, TECB.Create(1, 10))),
TVersion.Create($1B, 12, $1A, 10, $18, TECBlocks.Create(14,
TECB.Create(1, $10))), TVersion.Create($1C, 12, $24, 10, $10,
TECBlocks.Create($12, TECB.Create(1, $16))), TVersion.Create($1D, $10, $24,
14, $10, TECBlocks.Create($18, TECB.Create(1, $20))),
TVersion.Create(30, $10, $30, 14, $16, TECBlocks.Create($1C,
TECB.Create(1, $31))));
end;
/// <summary>
/// <p>Deduces version information from Data Matrix dimensions.</p>
///
/// <param name="numRows">Number of rows in modules</param>
/// <param name="numColumns">Number of columns in modules</param>
/// <returns>Version for a Data Matrix Code of those dimensions</returns>
/// <exception cref="FormatException">if dimensions do correspond to a valid Data Matrix size</exception>
/// </summary>
class function TVersion.getVersionForDimensions(numRows: Integer;
numColumns: Integer): TVersion;
var
Version: TVersion;
begin
Result := nil;
if (((numRows and $01) = 0) and ((numColumns and $01) = 0)) then
begin
for Version in TVersion.buildVersions do
begin
if ((Version.symbolSizeRows = numRows) and
(Version.symbolSizeColumns = numColumns)) then
begin
Result := Version;
break;
end;
end;
end;
end;
function TVersion.ToString: string;
begin
Result := versionNumber.ToString();
end;
Initialization
TVersion.ClassInit();
Finalization
TVersion.ClassFinal();
end.
@@ -0,0 +1,361 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.BitMatrixParser;
interface
uses
SysUtils,
Generics.Collections,
ZXing.DecodeHintType,
ZXing.Common.BitMatrix,
ZXing.QrCode.Internal.Version,
ZXing.QrCode.Internal.FormatInformation,
ZXing.Common.Detector.MathUtils,
ZXing.QrCode.Internal.DataMask;
type
TBitMatrixParser = class
private
BitMatrix: TBitMatrix;
mirrored: boolean;
parsedVersion: TVersion;
function copyBit(const i, j, versionBits: Integer): Integer;
public
parsedFormatInfo: TFormatInformation;
constructor Create(const BitMatrix: TBitMatrix);
/// <param name="bitMatrix">{@link TBitMatrix} to parse</param>
/// <throws>ReaderException if dimension is not >= 21 and 1 mod 4</throws>
class function createBitMatrixParser(
const bitMatrix: TBitMatrix): TBitMatrixParser; static;
(** Mirror the bit matrix in order to attempt a second reading. *)
procedure mirror;
(**
* Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
*)
procedure remask;
(**
* Prepare the parser for a mirrored operation.
* This flag has effect only on the {@link #readFormatInformation()} and the
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
* {@link #mirror()} method should be called.
*
* @param mirror Whether to read version and format information mirrored.
*)
procedure setMirror(const mirror: Boolean);
/// <summary> <p>Reads format information from one of its two locations within the QR Code.</p>
///
/// </summary>
/// <returns> {@link FormatInformation} encapsulating the QR Code's format info
/// </returns>
/// <throws> ReaderException if both format information locations cannot be parsed as </throws>
/// <summary> the valid encoding of format information
/// </summary>
function readFormatInformation: TFormatInformation;
/// <summary> <p>Reads version information from one of its two locations within the QR Code.</p>
///
/// </summary>
/// <returns> {@link Version} encapsulating the QR Code's version
/// </returns>
/// <throws> ReaderException if both version information locations cannot be parsed as </throws>
/// <summary> the valid encoding of version information
/// </summary>
function readVersion: TVersion;
// <summary> <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
// correct order in order to reconstruct the codewords bytes contained within the
/// QR Code.</p>
///
/// </summary>
/// <returns> bytes encoded within the QR Code
/// </returns>
/// <throws> ReaderException if the exact number of bytes expected is not read </throws>
function readCodewords: TArray<Byte>;
end;
implementation
{ TBitMatrixParser }
constructor TBitMatrixParser.Create(const bitMatrix: TBitMatrix);
begin
// Should only be called from createBitMatrixParser with the important checks before
Self.bitMatrix := BitMatrix;
end;
class function TBitMatrixParser.createBitMatrixParser(
const bitMatrix: TBitMatrix): TBitMatrixParser;
var
dimension: Integer;
begin
dimension := BitMatrix.Height;
if ((dimension < 21) and ((dimension and $03) <> 1))
then
Result := nil
else
Result := TBitMatrixParser.Create(bitMatrix);
end;
function TBitMatrixParser.copyBit(const i, j, versionBits: Integer): Integer;
var
bit: Boolean;
begin
if (mirrored)
then
bit := BitMatrix[j, i]
else
bit := BitMatrix[i, j];
if (bit)
then
Result := ((versionBits shl 1) or $1)
else
Result := (versionBits shl 1);
end;
procedure TBitMatrixParser.mirror;
var
x, y: Integer;
begin
for x := 0 to Pred(bitMatrix.width) do
for y := (x + 1) to Pred(bitMatrix.height) do
if (bitMatrix[x, y] <> bitMatrix[y, x]) then
begin
bitMatrix.flip(y, x);
bitMatrix.flip(x, y);
end;
end;
function TBitMatrixParser.readCodewords: TArray<Byte>;
var
formatInfo: TFormatInformation;
dimension, resultOffset,
currentByte, bitsRead,
j, i, count, col: Integer;
functionPattern: TBitMatrix;
readingUp: boolean;
Version: TVersion;
DataMask: TDataMask;
begin
Result := nil;
formatInfo := readFormatInformation;
if (formatInfo = nil)
then
exit;
Version := readVersion;
if (Version = nil)
then
exit;
// Get the data mask for the format used in this QR Code. This will exclude
// some bits from reading as we wind through the bit matrix.
DataMask := TDataMask.forReference(formatInfo.DataMask);
dimension := bitMatrix.Height;
DataMask.unmaskBitMatrix(BitMatrix, dimension);
functionPattern := Version.buildFunctionPattern;
try
readingUp := true;
Result := TArray<Byte>.Create();
SetLength(Result, version.TotalCodewords);
resultOffset := 0;
currentByte := 0;
bitsRead := 0;
j := (dimension - 1);
// Read columns in pairs, from right to left
while ((j > 0)) do
begin
if (j = 6)
then
// Skip whole column with vertical alignment pattern;
// saves time and makes the other code proceed more cleanly
Dec(j);
// Read alternatingly from bottom to top then top to bottom
for count := 0 to Pred(dimension) do
begin
if (readingUp)
then
i := ((dimension - 1) - count)
else
i := count;
for col := 0 to Pred(2) do
begin
// Ignore bits covered by the function pattern
if (not functionPattern[(j - col), i]) then
begin
// Read a bit
Inc(bitsRead);
currentByte := (currentByte shl 1);
if (bitMatrix[(j - col), i])
then
currentByte := (currentByte or 1);
// If we've made a whole byte, save it off
if (bitsRead = 8) then
begin
// if resultoffset > high(Result) then
// raise EProgrammerNotFound.CreateFmt('resultoffset %d> high(Result) %d',[resultOffset,high(Result)])
// else if resultoffset < 0 then
// raise EProgrammerNotFound.CreateFmt('resultoffset %d<0!',[resultOffset]);
if (resultoffset>=0) and (resultoffset <= high(Result)) then // minchiata aggiunta da carlo
Result[resultOffset] := Byte(currentByte);
Inc(resultOffset);
bitsRead := 0;
currentByte := 0;
end
end;
end;
end;
readingUp := not readingUp; // readingUp = !readingUp; // switch directions
Dec(j, 2);
end;
if (resultOffset <> version.TotalCodewords)
then
Result := nil;
finally
FreeAndNil(functionPattern);
end;
end;
function TBitMatrixParser.readFormatInformation: TFormatInformation;
var
formatInfoBits1,
formatInfoBits2,
dimension,
i, j, jMin: Integer;
begin
if (Self.parsedFormatInfo <> nil) then
begin
Result := Self.parsedFormatInfo;
exit;
end;
// Read top-left format info bits
formatInfoBits1 := 0;
for i := 0 to Pred(6) do
formatInfoBits1 := self.copyBit(i, 8, formatInfoBits1);
// .. and skip a bit in the timing pattern ...
formatInfoBits1 := self.copyBit(7, 8, formatInfoBits1);
formatInfoBits1 := self.copyBit(8, 8, formatInfoBits1);
formatInfoBits1 := self.copyBit(8, 7, formatInfoBits1);
// .. and skip a bit in the timing pattern ...
for j := 5 downto 0 do
formatInfoBits1 := self.copyBit(8, j, formatInfoBits1);
// Read the top-right/bottom-left pattern too
dimension := Self.bitMatrix.Height;
formatInfoBits2 := 0;
jMin := (dimension - 7);
for j := (dimension - 1) downto jMin do
formatInfoBits2 := self.copyBit(8, j, formatInfoBits2);
for i := (dimension - 8) to Pred(dimension) do
formatInfoBits2 := self.copyBit(i, 8, formatInfoBits2);
parsedFormatInfo := TFormatInformation.DecodeFormatInformation(formatInfoBits1, formatInfoBits2);
if (parsedFormatInfo <> nil)
then
Result := Self.parsedFormatInfo
else
Result := nil;
end;
function TBitMatrixParser.readVersion: TVersion;
var
dimension,
provisionalVersion,
versionBits,
i, j, ijMin: Integer;
begin
if (Self.parsedVersion <> nil) then
begin
Result := Self.parsedVersion;
exit;
end;
dimension := Self.bitMatrix.Height;
provisionalVersion := TMathUtils.Asr((dimension - 17), 2);
if (provisionalVersion <= 6) then
begin
Result := TVersion.getVersionForNumber(provisionalVersion);
exit;
end;
// Read top-right version info: 3 wide by 6 tall
versionBits := 0;
ijMin := (dimension - 11);
for j := 5 downto 0 do
for i := (dimension - 9) downto ijMin do
versionBits := Self.copyBit(i, j, versionBits);
parsedVersion := TVersion.decodeVersionInformation(versionBits);
if ((parsedVersion <> nil) and
(parsedVersion.DimensionForVersion = dimension)) then
begin
Result := parsedVersion;
exit;
end;
// Hmm, failed. Try bottom left: 6 wide by 3 tall
versionBits := 0;
for i := 5 downto 0 do
for j := (dimension - 9) downto ijMin do
versionBits := self.copyBit(i, j, versionBits);
parsedVersion := TVersion.decodeVersionInformation(versionBits);
if ((parsedVersion <> nil) and
(parsedVersion.DimensionForVersion = dimension))
then
Result := self.parsedVersion
else
Result := nil;
end;
procedure TBitMatrixParser.remask;
var
dimension: Integer;
begin
if (parsedFormatInfo <> nil) then
begin
dimension := bitMatrix.Height;
TDataMask.forReference(parsedFormatInfo.DataMask)
.unmaskBitMatrix(bitMatrix, dimension);
end;
end;
procedure TBitMatrixParser.setMirror(const mirror: Boolean);
begin
Self.parsedVersion := nil;
Self.parsedFormatInfo := nil;
Self.mirrored := mirror;
end;
end.
@@ -0,0 +1,187 @@
{
* 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.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.DataBlock;
interface
uses
System.SysUtils,
ZXing.QrCode.Internal.Version,
ZXing.QrCode.Internal.ErrorCorrectionLevel;
type
/// <summary> <p>Encapsulates a block of data within a QR Code. QR 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>
/// </summary>
TDataBlock = class sealed
private
Fcodewords: TArray<Byte>;
FnumDataCodewords: Integer;
public
constructor Create(const codewords: TArray<Byte>;
const NumDataCodewords: Integer);
destructor Destroy; override;
/// <summary> <p>When QR Codes use multiple data blocks, they are actually interleaved.
/// 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>
///
/// </summary>
/// <param name="rawCodewords">bytes as read directly from the QR Code
/// </param>
/// <param name="version">version of the QR Code
/// </param>
/// <param name="ecLevel">error-correction level of the QR Code
/// </param>
/// <returns> {@link TDataBlock}s containing original bytes, "de-interleaved" from representation in the
/// QR Code
/// </returns>
class function getDataBlocks(const rawCodewords: TArray<Byte>;
const version: TVersion; const ecLevel: TErrorCorrectionLevel): TArray<TDataBlock>; static;
property codewords : TArray<Byte> read Fcodewords;
property numDataCodewords : Integer read FnumDataCodewords;
end;
implementation
{ TDataBlock }
constructor TDataBlock.Create(const codewords: TArray<Byte>;
const numDataCodewords: Integer);
begin
Fcodewords := codewords;
FnumDataCodewords := numDataCodewords;
end;
destructor TDataBlock.Destroy;
begin
Fcodewords := nil;
inherited;
end;
class function TDataBlock.getDataBlocks(const rawCodewords: TArray<Byte>;
const version: TVersion; const ecLevel: TErrorCorrectionLevel): TArray<TDataBlock>;
var
numResultBlocks,
shorterBlocksTotalCodewords,
longerBlocksStartAt,
shorterBlocksNumDataCodewords,
max, i, j,
totalBlocks,
numBlockCodewords,
iOffset,
rawCodewordsOffset,
NumDataCodewords: Integer;
ecBlock: Tversion.TECB;
ecBlocks: Tversion.TECBlocks;
ecBlockArray: TArray<Tversion.TECB>;
numBlockCodewordsBytes: TArray<Byte>;
begin
if (Length(rawCodewords) <> Version.TotalCodewords)
then
raise EArgumentException.Create('Invalid arguments');
// Figure out the number and size of data blocks used by this version and
// error correction level
ecBlocks := version.getECBlocksForLevel(ecLevel);
// First count the total number of data blocks
totalBlocks := 0;
ecBlockArray := ecBlocks.getECBlocks;
for ecBlock in ecBlockArray do
Inc(totalBlocks, ecBlock.Count);
// Now establish DataBlocks of the appropriate size and number of data codewords
Result := TArray<TDataBlock>.Create();
SetLength(result, totalBlocks);
numResultBlocks := 0;
for ecBlock in ecBlockArray do
begin
for i := 0 to Pred(ecBlock.count) do
begin
numDataCodewords := ecBlock.DataCodewords;
numBlockCodewords := ecBlocks.ECCodewordsPerBlock + numDataCodewords;
numBlockCodewordsBytes := TArray<Byte>.Create();
SetLength(numBlockCodewordsBytes, numBlockCodewords);
Result[numResultBlocks] := TDataBlock.Create(numBlockCodewordsBytes, numDataCodewords);
Inc(numResultBlocks);
end
end;
numBlockCodewordsBytes := nil;
// All blocks have the same amount of data, except that the last n
// (where n may be 0) have 1 more byte. Figure out where these start.
shorterBlocksTotalCodewords := Length(Result[0].codewords);
longerBlocksStartAt := Length(result) - 1;
while ((longerBlocksStartAt >= 0)) do
begin
if (Length(Result[longerBlocksStartAt].codewords) = shorterBlocksTotalCodewords)
then
break;
Dec(longerBlocksStartAt);
end;
Inc(longerBlocksStartAt);
shorterBlocksNumDataCodewords := (shorterBlocksTotalCodewords - ecBlocks.ECCodewordsPerBlock);
// The last elements of result may be 1 element longer;
// first fill out as many elements as all of them have
rawCodewordsOffset := 0;
for i := 0 to Pred(shorterBlocksNumDataCodewords) do
begin
for j := 0 to Pred(numResultBlocks) do
begin
Result[j].codewords[i] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
end;
// Fill out the last data block in the longer ones
for j := longerBlocksStartAt to Pred(numResultBlocks) do
begin
result[j].codewords[shorterBlocksNumDataCodewords] :=
rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
// Now add in error correction blocks
max := Length(result[0].codewords);
for i := shorterBlocksNumDataCodewords to Pred(max) do
begin
for j := 0 to Pred(numResultBlocks) do
begin
if (j < longerBlocksStartAt)
then
iOffset := i
else
iOffset := i + 1;
Result[j].codewords[iOffset] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
end;
end;
end.
@@ -0,0 +1,226 @@
{
* 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.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.DataMask;
interface
uses
SysUtils,
ZXing.Common.BitMatrix,
ZXing.Common.Detector.MathUtils;
type
/// <summary> <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
/// of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
/// including areas used for finder patterns, timing patterns, etc. These areas should be unused
/// after the point they are unmasked anyway.</p>
///
/// <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
/// and j is row position. In fact, as the text says, i is row position and j is column position.</p>
/// </summary>
TDataMask = class abstract
private
/// <summary> See ISO 18004:2006 6.8.1</summary>
class var DATA_MASKS: TArray<TDataMask>;
class procedure InitializeClass;
class procedure FinalizeClass;
function isMasked(const i, j: Integer): Boolean; virtual; abstract;
public
/// <param name="reference">a value between 0 and 7 indicating one of the eight possible
/// data mask patterns a QR Code may use
/// </param>
/// <returns> {@link DataMask} encapsulating the data mask pattern
/// </returns>
class function forReference(const reference: Integer): TDataMask; static;
/// <summary> <p>Implementations of this method reverse the data masking process applied to a QR Code and
/// make its bits ready to read.</p>
/// </summary>
/// <param name="bits">representation of QR Code bits
/// </param>
/// <param name="dimension">dimension of QR Code, represented by bits, being unmasked
/// </param>
procedure unmaskBitMatrix(const bits: TBitMatrix; const dimension: Integer);
end;
/// <summary> 000: mask bits for which (x + y) mod 2 == 0</summary>
TDataMask000 = class(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 001: mask bits for which x mod 2 == 0</summary>
TDataMask001 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 010: mask bits for which y mod 3 == 0</summary>
TDataMask010 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 011: mask bits for which (x + y) mod 3 == 0</summary>
TDataMask011 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 100: mask bits for which (x/2 + y/3) mod 2 == 0</summary>
TDataMask100 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 101: mask bits for which xy mod 2 + xy mod 3 == 0</summary>
TDataMask101 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0</summary>
TDataMask110 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0</summary>
TDataMask111 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
implementation
{ TDataMask }
class procedure TDataMask.InitializeClass;
begin
TDataMask.DATA_MASKS := TArray<TDataMask>.Create(TDataMask000.Create,
TDataMask001.Create,
TDataMask010.Create,
TDataMask011.Create,
TDataMask100.Create,
TDataMask101.Create,
TDataMask110.Create,
TDataMask111.Create);
end;
class procedure TDataMask.FinalizeClass;
var
dam: TDataMask;
begin
for dam in TDataMask.DATA_MASKS do
dam.Free;
DATA_MASKS := nil;
end;
class function TDataMask.forReference(const reference: Integer): TDataMask;
begin
if ((reference < 0) or (reference > 7))
then
raise EArgumentException.Create('Invalid arguments');
Result := TDataMask.DATA_MASKS[reference];
end;
procedure TDataMask.unmaskBitMatrix(const bits: TBitMatrix;
const dimension: Integer);
var
i, j: Integer;
begin
for i := 0 to Pred(dimension) do
for j := 0 to Pred(dimension) do
if (self.isMasked(i, j))
then
bits.flip(j, i);
end;
{ TDataMask000 }
function TDataMask000.isMasked(const i, j: Integer): Boolean;
begin
Result := ((i + j) and $01) = 0;
end;
{ TDataMask001 }
function TDataMask001.isMasked(const i, j: Integer): Boolean;
begin
Result := (i and $01) = 0;
end;
{ TDataMask010 }
function TDataMask010.isMasked(const i, j: Integer): Boolean;
begin
Result := (j mod 3) = 0;
end;
{ TDataMask011 }
function TDataMask011.isMasked(const i, j: Integer): Boolean;
begin
Result := (((i + j) mod 3) = 0)
end;
{ TDataMask100 }
function TDataMask100.isMasked(const i, j: Integer): Boolean;
begin
Result := (( TMathUtils.Asr(UInt32(i),1) + (j div 3)) and $01) = 0;
end;
{ TDataMask101 }
function TDataMask101.isMasked(const i, j: Integer): Boolean;
var
temp: Integer;
begin
temp := (i * j);
Result := ((temp and $01) + (temp mod 3)) = 0;
end;
{ TDataMask110 }
function TDataMask110.isMasked(const i, j: Integer): Boolean;
var
temp: Integer;
begin
temp := (i * j);
Result := (((temp and $01) + (temp mod 3)) and 1) = 0;
end;
{ TDataMask111 }
function TDataMask111.isMasked(const i, j: Integer): Boolean;
begin
Result := ((((i + j) and $01) + ((i * j) mod 3)) and $01) = 0;
end;
initialization
TDataMask.InitializeClass;
finalization
TDataMask.FinalizeClass;
end.
@@ -0,0 +1,587 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.DecodedBitStreamParser;
interface
uses
ZXing.BitSource,
SysUtils,
ZXing.DecodeHintType,
Generics.Collections,
ZXing.QrCode.Internal.ErrorCorrectionLevel,
ZXing.QrCode.Internal.Version,
ZXing.DecoderResult,
ZXing.CharacterSetECI,
ZXing.StringUtils,
ZXing.ByteSegments,
ZXing.QrCode.Internal.Mode,
ZXing.Common.Detector.MathUtils;
type
/// <summary> <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
/// in one QR Code. This class decodes the bits back into text.</p>
///
/// <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
/// </summary>
TDecodedBitStreamParser = class abstract
private
/// <summary>
/// See ISO 18004:2006, 6.4.4 Table 5
/// </summary>
class var ALPHANUMERIC_CHARS: TArray<Char>;
const
GB2312_SUBSET: Integer = 1;
class procedure InitializeClass;
class procedure FinalizeClass;
class function decodeAlphanumericSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer;
const fc1InEffect: Boolean): Boolean; static;
class function decodeByteSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer;
const currentCharacterSetECI: TCharacterSetECI;
const byteSegments: IByteSegments;
const hints: TDictionary<TDecodeHintType, TObject>): Boolean; static;
/// <summary>
/// See specification GBT 18284-2000
/// </summary>
/// <param name="bits">The bits.</param>
/// <param name="result">The result.</param>
/// <param name="count">The count.</param>
/// <returns></returns>
class function decodeHanziSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer): Boolean; static;
class function decodeKanjiSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer): boolean; static;
class function decodeNumericSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer): Boolean; static;
class function parseECIValue(const bits: TBitSource): Integer; static;
class function toAlphaNumericChar(const value: Integer): Char; static;
public
class function decode(const bytes: TArray<Byte>; const version: TVersion;
const ecLevel: TErrorCorrectionLevel;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult; static;
end;
implementation
{ TDecodedBitStreamParser }
class procedure TDecodedBitStreamParser.InitializeClass;
begin
ALPHANUMERIC_CHARS := TArray<Char>.Create('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', ' ',
'$', '%', '*', '+', '-', '.', '/', ':');
end;
class procedure TDecodedBitStreamParser.FinalizeClass;
begin
ALPHANUMERIC_CHARS := nil;
end;
class function TDecodedBitStreamParser.decode(const bytes: TArray<Byte>;
const version: TVersion; const ecLevel: TErrorCorrectionLevel;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult;
var
Mode: TMode;
bits: TBitSource;
byteSegments: IByteSegments;
fc1InEffect: Boolean;
symbolSequence,
parityData,
count, value,
countHanzi, subSet: Integer;
currentCharacterSetECI: TCharacterSetECI;
ecstring,s: String;
res: TStringBuilder;
begin
Result := nil;
bits := TBitSource.Create(bytes);
res := TStringBuilder.Create(50);
byteSegments := ByteSegmentsCreate();
byteSegments.Capacity := 1;
symbolSequence := -1;
parityData := -1;
try
try
currentCharacterSetECI := nil;
fc1InEffect := false;
repeat
// While still another segment to read...
if (bits.available < 4)
then
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
Mode := TMode.TERMINATOR
else
begin
try
Mode := TMode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
except
on E: EArgumentException do
exit;
end;
end;
if (Mode <> TMode.TERMINATOR) then
begin
if ((Mode = TMode.FNC1_FIRST_POSITION) or
(Mode = TMode.FNC1_SECOND_POSITION)) then
begin
// We do little with FNC1 except alter the parsed result a bit according to the spec
fc1InEffect := true;
end else
begin
if (Mode = TMode.STRUCTURED_APPEND) then
begin
if (bits.available() < 16)
then
exit;
// not really supported; but sequence number and parity is added later to the result metadata
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
symbolSequence := bits.readBits(8);
parityData := bits.readBits(8);
end else
begin
if (Mode = TMode.ECI) then
begin
// Count doesn't apply to ECI
value := parseECIValue(bits);
currentCharacterSetECI := TCharacterSetECI.getCharacterSetECIByValue(value);
if (currentCharacterSetECI = nil)
then
exit;
end else
begin
// First handle Hanzi mode which does not start with character count
if (Mode = TMode.HANZI) then
begin
// chinese mode contains a sub set indicator right after mode indicator
subSet := bits.readBits(4);
countHanzi := bits.readBits(Mode.getCharacterCountBits(version));
if (subSet = GB2312_SUBSET) then
begin
if (not TDecodedBitStreamParser.decodeHanziSegment(bits,
res, countHanzi))
then
exit;
end;
end else
begin
// "Normal" QR code modes:
// How many characters will follow, encoded in this mode?
count := bits.readBits(Mode.getCharacterCountBits(version));
if (Mode = TMode.NUMERIC) then
begin
if (not TDecodedBitStreamParser.decodeNumericSegment(bits,
res, count))
then
exit;
end else
begin
if (Mode = TMode.ALPHANUMERIC) then
begin
if (not TDecodedBitStreamParser.
decodeAlphanumericSegment(bits, res, count, fc1InEffect))
then
exit;
end else
begin
if (Mode = TMode.BYTE) then
begin
if (not TDecodedBitStreamParser.
decodeByteSegment(bits, res, count,
currentCharacterSetECI, byteSegments, hints))
then
exit;
end else
begin
if (Mode <> TMode.KANJI) then
begin
if (not TDecodedBitStreamParser.
decodeKanjiSegment(bits, res, count))
then
exit;
end else exit;
end;
end;
end;
end;
end;
end;
end;
end;
until (Mode = TMode.TERMINATOR)
except
on E: EArgumentException do
exit;
end;
if (byteSegments.count = 0)
then
byteSegments := nil;
if (ecLevel = nil)
then
ecstring := ''
else
ecstring := ecLevel.toString();
s:= res.toString.Replace(#13+#10, #10).Replace(#10, #13);
Result := TDecoderResult.Create(bytes, s, byteSegments, ecstring, symbolSequence,
parityData);
finally
// FreeAndNil(byteSegments);
res.Clear();
FreeAndNil(res);
FreeAndNil(bits);
end;
end;
class function TDecodedBitStreamParser.decodeAlphanumericSegment(
const bits: TBitSource; const res: TStringBuilder; count: Integer;
const fc1InEffect: Boolean): Boolean;
var
start, i, nextTwoCharsBits: Integer;
charArray: TArray<Char>;
begin
Result := false;
// Read two characters at a time
start := res.Length;
while ((count > 1)) do
begin
if (bits.available < 11)
then
exit;
nextTwoCharsBits := bits.readBits(11);
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((nextTwoCharsBits div 45)));
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((nextTwoCharsBits mod 45)));
Dec(count, 2);
end;
if (count = 1) then
begin
// special case: one character left
if (bits.available < 6)
then
exit;
res.Append(TDecodedBitStreamParser.toAlphaNumericChar(bits.readBits(6)))
end;
// See section 6.4.8.1, 6.4.8.2
if (fc1InEffect) then
begin
// We need to massage the result a bit if in an FNC1 mode:
for i := start to Pred(res.Length) do
begin
if (res.Chars[i] = '%') then
begin
if ((i < (res.Length - 1)) and (res.Chars[(i + 1)] = '%'))
then
// %% is rendered as %
res.Remove((i + 1), 1)
else
begin
// In alpha mode, % should be converted to FNC1 separator 0x1D
res.Remove(i, 1);
charArray := TArray<Char>.Create(Char($1D));
res.Insert(i, charArray);
end;
end;
end;
end;
Result := true;
end;
class function TDecodedBitStreamParser.decodeByteSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer;
const currentCharacterSetECI: TCharacterSetECI;
const byteSegments: IByteSegments;
const hints: TDictionary<TDecodeHintType, TObject>): Boolean;
var
Enc:TEncoding;
encodingS, s: string;
readBytes: TArray<Byte>;
i: Integer;
begin
Result := false;
// Don't crash trying to read more bits than we have available.
if ((count shl 3) > bits.available)
then
exit;
readBytes := TArray<Byte>.Create();
SetLength(readBytes, count);
for i := 0 to Pred(count) do
readBytes[i] := Byte(bits.readBits(8));
if (currentCharacterSetECI = nil)
then
// The spec isn't clear on this mode; see
// section 6.4.5: t does not say which encoding to assuming
// upon decoding. I have seen ISO-8859-1 used as well as
// Shift_JIS -- without anything like an ECI designator to
// give a hint.
encodingS := TStringUtils.guessEncoding(readBytes, hints)
else
encodingS := currentCharacterSetECI.EncodingName;
try
enc := TEncoding.GetEncoding(encodingS);
try
s := enc.GetString(readBytes, 0, Length(readBytes));
res.Append(s);
finally
FreeAndNil(enc);
end;
except
on E: Exception do
exit;
end;
byteSegments.Add(readBytes);
result := true;
end;
class function TDecodedBitStreamParser.decodeHanziSegment(
const bits: TBitSource; const res: TStringBuilder; count: Integer): Boolean;
var
buffer: TArray<Byte>;
offset, twoBytes,
assembledTwoBytes: Integer;
begin
Result := false;
// Don't crash trying to read more bits than we have available.
if ((count * 13) > bits.available)
then
exit;
// Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as GB2312 afterwards
buffer := TArray<Byte>.Create();
SetLength(buffer, 2 * count);
offset := 0;
while ((count > 0)) do
begin
// Each 13 bits encodes a 2-byte character
twoBytes := bits.readBits(13);
assembledTwoBytes := (((twoBytes div $60) shl 8) or (twoBytes mod $60));
if (assembledTwoBytes < $00A00)
then
// In the 0xA1A1 to 0xAAFE range
Inc(assembledTwoBytes, $0A1A1)
else
// In the 0xB0A1 to 0xFAFE range
Inc(assembledTwoBytes, $0A6A1);
buffer[offset] := Byte(TMathUtils.Asr(assembledTwoBytes, 8) and $FF);
buffer[(offset + 1)] := Byte(assembledTwoBytes and $FF);
Inc(offset, 2);
Dec(count);
end;
try
res.Append(Tencoding.GetEncoding(TStringUtils.GB2312).GetString(buffer, 0,
Length(buffer)))
except
on E: Exception do
exit;
end;
Result := true;
end;
class function TDecodedBitStreamParser.decodeKanjiSegment(
const bits: TBitSource; const res: TStringBuilder; count: Integer): Boolean;
var
buffer: TArray<Byte>;
twoBytes, offset,
assembledTwoBytes: Integer;
begin
Result := false;
// Don't crash trying to read more bits than we have available.
if ((count * 13) > bits.available)
then
exit;
// Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as Shift_JIS afterwards
buffer := TArray<Byte>.Create();
SetLength(buffer, 2 * count);
offset := 0;
while ((count > 0)) do
begin
// Each 13 bits encodes a 2-byte character
twoBytes := bits.readBits(13);
assembledTwoBytes := (((twoBytes div $0C0) shl 8) or (twoBytes mod $0C0));
if (assembledTwoBytes < $1F00)
then
// In the 0x8140 to 0x9FFC range
Inc(assembledTwoBytes, $08140)
else
Inc(assembledTwoBytes, $0C140);
buffer[offset] := Byte( TMathUtils.Asr(assembledTwoBytes,8));
buffer[(offset + 1)] := Byte(assembledTwoBytes);
Inc(offset, 2);
Dec(count);
end;
try
res.Append(Tencoding.GetEncoding(TStringUtils.SHIFT_JIS).GetString(buffer,
0, Length(buffer)))
except
on E: Exception do
exit;
end;
Result := true;
end;
class function TDecodedBitStreamParser.decodeNumericSegment(
const bits: TBitSource; const res: TStringBuilder; count: Integer): Boolean;
var
threeDigitsBits,
twoDigitsBits,
digitBits: Integer;
begin
Result := false;
// Read three digits at a time
while ((count >= 3)) do
begin
if (bits.available < 10)
then
exit;
threeDigitsBits := bits.readBits(10);
if (threeDigitsBits >= 1000)
then
exit;
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((threeDigitsBits div 100)));
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
(((threeDigitsBits div 10) mod 10)));
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((threeDigitsBits mod 10)));
Dec(count, 3)
end;
if (count = 2) then
begin
// Two digits left over to read, encoded in 7 bits
if (bits.available < 7)
then
exit;
twoDigitsBits := bits.readBits(7);
if (twoDigitsBits >= 100)
then
exit;
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((twoDigitsBits div 10)));
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((twoDigitsBits mod 10)));
end else
begin
if (count = 1) then
begin
// One digit left over to read
if (bits.available < 4)
then
exit;
digitBits := bits.readBits(4);
if (digitBits >= 10)
then
exit;
res.Append(TDecodedBitStreamParser.toAlphaNumericChar(digitBits))
end;
end;
Result := true;
end;
class function TDecodedBitStreamParser.parseECIValue(
const bits: TBitSource): Integer;
var
firstByte,
secondByte,
SecondThirdBytes: Integer;
begin
firstByte := bits.readBits(8);
if ((firstByte and $80) = 0) then
begin
// just one byte
Result := (firstByte and $7F);
exit;
end;
if ((firstByte and $C0) = $80) then
begin
// two bytes
secondByte := bits.readBits(8);
Result := (((firstByte and $3F) shl 8) or secondByte);
exit;
end;
if ((firstByte and $E0) = $C0) then
begin
// three bytes
SecondThirdBytes := bits.readBits(16);
Result := (((firstByte and $1F) shl 16) or SecondThirdBytes);
exit;
end;
raise EArgumentException.Create('Bad ECI bits starting with byte ' +
firstByte.toString());
end;
class function TDecodedBitStreamParser.toAlphaNumericChar(
const value: Integer): Char;
begin
if (value >= Length(ALPHANUMERIC_CHARS))
then
raise Exception.Create('Format exception');
Result := ALPHANUMERIC_CHARS[value];
end;
initialization
TDecodedBitStreamParser.InitializeClass;
finalization
TDecodedBitStreamParser.FinalizeClass;
end.
@@ -0,0 +1,278 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E.Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.Decoder;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.DecodeHintType,
ZXing.Common.BitMatrix,
ZXing.QrCode.Internal.BitMatrixParser,
ZXing.DecoderResult,
ZXing.Common.ReedSolomon.ReedSolomonDecoder,
ZXing.Common.ReedSolomon.GenericGF,
ZXing.QrCode.Internal.QRCodeDecoderMetaData,
ZXing.QrCode.Internal.Version,
ZXing.QrCode.Internal.FormatInformation,
ZXing.QrCode.Internal.ErrorCorrectionLevel,
ZXing.QrCode.Internal.DataBlock,
ZXing.QrCode.Internal.DecodedBitStreamParser;
type
/// <summary>
/// <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
/// the QR Code from an image.</p>
/// </summary>
TQRDecoder = class
private
rsDecoder: TReedSolomonDecoder;
/// <summary>
/// <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>
/// </summary>
/// <param name="codewordBytes">data and error correction codewords</param>
/// <param name="numDataCodewords">number of codewords that are data bytes</param>
/// <returns></returns>
function correctErrors(const codewordBytes: TArray<Byte>;
const numDataCodewords: Integer): Boolean;
function decode(const parser: TBitMatrixParser;
const hints: TDictionary<TDecodeHintType, TObject>)
: TDecoderResult; overload;
public
/// <summary>
/// Initializes a new instance of the <see cref="Decoder"/> class.
/// </summary>
constructor Create;
destructor Destroy; override;
/// <summary>
/// <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
/// "true" is taken to mean a black module.</p>
/// </summary>
/// <param name="image">booleans representing white/black QR Code modules</param>
/// <param name="hints">decoding hints that should be used to influence decoding</param>
/// <returns>
/// text and bytes encoded within the QR Code
/// </returns>
function decode(const image: TArray<TArray<Boolean>>;
const hints: TDictionary<TDecodeHintType, TObject>)
: TDecoderResult; overload;
/// <summary>
/// <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
/// </summary>
/// <param name="bits">booleans representing white/black QR Code modules</param>
/// <param name="hints">decoding hints that should be used to influence decoding</param>
/// <returns>
/// text and bytes encoded within the QR Code
/// </returns>
function decode(const bits: TBitMatrix;
const hints: TDictionary<TDecodeHintType, TObject>)
: TDecoderResult; overload;
end;
implementation
{ TQRDecoder }
constructor TQRDecoder.Create;
begin
rsDecoder := TReedSolomonDecoder.Create(TGenericGF.QR_CODE_FIELD_256);
end;
destructor TQRDecoder.Destroy;
begin
FreeAndNil(rsDecoder);
inherited;
end;
function TQRDecoder.correctErrors(const codewordBytes: TArray<Byte>;
const numDataCodewords: Integer): Boolean;
var
i, numCodewords, numECCodewords: Integer;
codewordsInts: TArray<Integer>;
begin
Result := false;
numCodewords := Length(codewordBytes);
// First read into an array of ints
codewordsInts := TArray<Integer>.Create();
SetLength(codewordsInts, numCodewords);
for i := 0 to Pred(numCodewords) do
codewordsInts[i] := (codewordBytes[i] and $FF);
numECCodewords := Length(codewordBytes) - numDataCodewords;
if (not rsDecoder.decode(codewordsInts, numECCodewords)) then
exit;
// 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 i := 0 to Pred(numDataCodewords) do
codewordBytes[i] := Byte(codewordsInts[i]);
Result := true;
end;
function TQRDecoder.decode(const image: TArray<TArray<Boolean>>;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult;
var
dimension, i, j: Integer;
bits: TBitMatrix;
begin
dimension := Length(image);
bits := TBitMatrix.Create(dimension);
try
for i := 0 to Pred(dimension) do
for j := 0 to Pred(dimension) do
bits[j, i] := image[i][j];
Result := decode(bits, hints);
finally
bits.Free;
end;
end;
function TQRDecoder.decode(const bits: TBitMatrix;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult;
var
parser: TBitMatrixParser;
begin
Result := nil;
// Construct a parser and read version, error-correction level
parser := TBitMatrixParser.createBitMatrixParser(bits);
if Assigned(parser) then
begin
try
Result := decode(parser, hints);
if (Result = nil) then
begin
// Revert the bit matrix
parser.remask;
// Will be attempting a mirrored reading of the version and format info.
parser.setMirror(true);
// Preemptively read the version.
if (parser.readVersion = nil) then
exit;
// Preemptively read the format information.
if (parser.readFormatInformation = nil) then
exit;
(*
* Since we're here, this means we have successfully detected some kind
* of version and format information when mirrored. This is a good sign,
* that the QR code may be mirrored, and we should try once more with a
* mirrored content.
*)
// Prepare for a mirrored reading.
parser.mirror;
Result := decode(parser, hints);
if (Result <> nil) then
// Success! Notify the caller that the code was mirrored.
Result.Other := TQRCodeDecoderMetaData.Create(true);
end;
finally
parser.Free;
end;
end;
end;
function TQRDecoder.decode(const parser: TBitMatrixParser;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult;
var
DataBlock: TDataBlock;
dataBlocks: TArray<TDataBlock>;
Version: TVersion;
formatInfo: TFormatInformation;
ecLevel: TErrorCorrectionLevel;
codeWords, resultBytes, codewordBytes: TArray<Byte>;
totalBytes, resultOffset, i, numDataCodewords: Integer;
begin
Result := nil;
Version := parser.readVersion;
if (Version = nil) then
exit;
formatInfo := parser.readFormatInformation;
if (formatInfo = nil) then
exit;
ecLevel := formatInfo.ErrorCorrectionLevel;
// Read codewords
codeWords := parser.readCodewords;
if (codeWords = nil) then
begin
FreeAndNil(formatInfo);
exit;
end;
// Separate into data blocks
dataBlocks := TDataBlock.getDataBlocks(codeWords, Version, ecLevel);
// Count total number of data bytes
totalBytes := 0;
for DataBlock in dataBlocks do
Inc(totalBytes, DataBlock.numDataCodewords);
resultBytes := TArray<Byte>.Create();
SetLength(resultBytes, totalBytes);
try
resultOffset := 0;
// Error-correct and copy data blocks together into a stream of bytes
for DataBlock in dataBlocks do
begin
codewordBytes := DataBlock.codeWords;
numDataCodewords := DataBlock.numDataCodewords;
if (not self.correctErrors(codewordBytes, numDataCodewords)) then
begin
exit(nil);
end;
for i := 0 to Pred(numDataCodewords) do
begin
resultBytes[resultOffset] := codewordBytes[i];
Inc(resultOffset);
end;
end;
// Decode the contents of that stream of bytes
Result := TDecodedBitStreamParser.decode(resultBytes, Version,
ecLevel, hints);
finally
for DataBlock in dataBlocks do
DataBlock.Free;
FreeAndNil(formatInfo);
end;
end;
end.
@@ -0,0 +1,149 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.ErrorCorrectionLevel;
interface
uses SysUtils;
type
/// <summary>
/// <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
/// defined by the QR code standard.</p>
/// </summary>
TErrorCorrectionLevel = class sealed
private
Fbits: Integer;
Fname: string;
Fordinal_Renamed_Field: Integer;
class var FOR_BITS: TArray<TErrorCorrectionLevel>;
class procedure InitalizeClass; static;
class procedure FinalizeClass; static;
public
constructor Create(const ordinal, bits: Integer; const name: String);
/// <summary>
/// Fors the bits.
/// </summary>
/// <param name="bits">int containing the two bits encoding a QR Code's error correction level</param>
/// <returns>
/// <see cref="ErrorCorrectionLevel"/> representing the encoded error correction level
/// </returns>
class function forBits(const bits: Integer): TErrorCorrectionLevel; static;
/// <summary> L = ~7% correction</summary>
class var L: TErrorCorrectionLevel;
/// <summary> M = ~15% correction</summary>
class var M: TErrorCorrectionLevel;
/// <summary> Q = ~25% correction</summary>
class var Q: TErrorCorrectionLevel;
/// <summary> H = ~30% correction</summary>
class var H: TErrorCorrectionLevel;
/// <summary>
/// Ordinals this instance.
/// </summary>
/// <returns></returns>
function ordinal: Integer;
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
function ToString: String; override;
/// <summary>
/// Gets the bits.
/// </summary>
property Bits: Integer read Fbits;
/// <summary>
/// Gets the name.
/// </summary>
property Name: String read Fname;
end;
implementation
{ TErrorCorrectionLevel }
constructor TErrorCorrectionLevel.Create(const ordinal, bits: Integer;
const name: String);
begin
Fordinal_Renamed_Field := ordinal;
Fbits := bits;
Fname := name
end;
class procedure TErrorCorrectionLevel.InitalizeClass;
begin
H := TErrorCorrectionLevel.Create(3, 2, 'H');
L := TErrorCorrectionLevel.Create(0, 1, 'L');
M := TErrorCorrectionLevel.Create(1, 0, 'M');
Q := TErrorCorrectionLevel.Create(2, 3, 'Q');
FOR_BITS := TArray<TErrorCorrectionLevel>.Create(M, L, H, Q);
end;
class procedure TErrorCorrectionLevel.FinalizeClass;
begin
H.FOR_BITS := nil;
L.FOR_BITS := nil;
M.FOR_BITS := nil;
Q.FOR_BITS := nil;
FreeAndNil(H);
FreeAndNil(L);
FreeAndNil(M);
FreeAndNil(Q);
FOR_BITS := nil;
end;
class function TErrorCorrectionLevel.forBits(const bits: Integer): TErrorCorrectionLevel;
begin
if ((bits < 0) or
(bits >= Length(TErrorCorrectionLevel.FOR_BITS)))
then
raise EArgumentException.Create('Invalid argument');
Result := TErrorCorrectionLevel.FOR_BITS[bits];
end;
function TErrorCorrectionLevel.ordinal: Integer;
begin
Result := Fordinal_Renamed_Field;
end;
function TErrorCorrectionLevel.ToString: string;
begin
Result := Fname
end;
initialization
TErrorCorrectionLevel.InitalizeClass;
finalization
TErrorCorrectionLevel.FinalizeClass;
end.
@@ -0,0 +1,256 @@
{
* 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.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.FormatInformation;
interface
uses
Generics.Collections,
ZXing.QrCode.Internal.ErrorCorrectionLevel,
Classes,
ZXing.Common.Detector.MathUtils;
type
/// <summary> <p>Encapsulates a QR Code's format information, including the data mask used and
/// error correction level.</p>
///
/// </summary>
/// <seealso cref="TDataMask">
/// </seealso>
/// <seealso cref="TErrorCorrectionLevel">
/// </seealso>
TFormatInformation = class sealed
private
FErrorCorrectionLevel: TErrorCorrectionLevel;
FDataMask: Byte;
const
FORMAT_INFO_MASK_QR: Integer = $5412;
/// <summary> See ISO 18004:2006, Annex C, Table C.1</summary>
class var FORMAT_INFO_DECODE_LOOKUP: TArray<TArray<Integer>>;
/// <summary> Offset i holds the number of 1 bits in the binary representation of i</summary>
class var BITS_SET_IN_HALF_BYTE: TArray<Integer>;
class procedure InitializeClass; static;
class procedure FinalizeClass; static;
class function doDecodeFormatInformation(const maskedFormatInfo1,
maskedFormatInfo2: Integer): TFormatInformation; static;
public
constructor Create(const formatInfo: Integer);
function Equals(o: TObject): Boolean; override;
function GetHashCode: Integer; override;
class function numBitsDiffering(a, b: Integer): Integer; static;
/// <summary>
/// Decodes the format information.
/// </summary>
/// <param name="maskedFormatInfo1">format info indicator, with mask still applied</param>
/// <param name="maskedFormatInfo2">The masked format info2.</param>
/// <returns>
/// information about the format it specifies, or <code>null</code>
/// if doesn't seem to match any known pattern
/// </returns>
class function decodeFormatInformation(const maskedFormatInfo1,
maskedFormatInfo2: Integer): TFormatInformation; static;
property ErrorCorrectionLevel: TErrorCorrectionLevel
read FErrorCorrectionLevel;
property DataMask: Byte read FDataMask;
end;
implementation
{ TFormatInformation }
constructor TFormatInformation.Create(const formatInfo: Integer);
begin
// Bits 3,4
FErrorCorrectionLevel := TErrorCorrectionLevel.forBits
(TMathUtils.Asr(formatInfo, 3) and $03);
// Bottom 3 bits
FDataMask := Byte(formatInfo and 7);
end;
class function TFormatInformation.decodeFormatInformation
(const maskedFormatInfo1, maskedFormatInfo2: Integer): TFormatInformation;
var
formatInfo: TFormatInformation;
begin
formatInfo := doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
if (formatInfo <> nil) then
Result := formatInfo
else
// Should return null, but, some QR codes apparently
// do not mask this info. Try again by actually masking the pattern
// first
Result := TFormatInformation.doDecodeFormatInformation
((maskedFormatInfo1 xor FORMAT_INFO_MASK_QR),
(maskedFormatInfo2 xor FORMAT_INFO_MASK_QR));
end;
class function TFormatInformation.doDecodeFormatInformation
(const maskedFormatInfo1, maskedFormatInfo2: Integer): TFormatInformation;
var
bestDifference, bestFormatInfo, bitsDifference, targetInfo: Integer;
decodeInfo: TArray<Integer>;
begin
Result := nil;
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
bestDifference := High(Integer);
bestFormatInfo := 0;
for decodeInfo in FORMAT_INFO_DECODE_LOOKUP do
begin
targetInfo := decodeInfo[0];
if ((targetInfo = maskedFormatInfo1) or (targetInfo = maskedFormatInfo2))
then
begin
// Found an exact match
Result := TFormatInformation.Create(decodeInfo[1]);
exit;
end;
bitsDifference := numBitsDiffering(maskedFormatInfo1, targetInfo);
if (bitsDifference < bestDifference) then
begin
bestFormatInfo := decodeInfo[1];
bestDifference := bitsDifference;
end;
if (maskedFormatInfo1 <> maskedFormatInfo2) then
begin
// also try the other option
bitsDifference := numBitsDiffering(maskedFormatInfo2, targetInfo);
if (bitsDifference < bestDifference) then
begin
bestFormatInfo := decodeInfo[1];
bestDifference := bitsDifference;
end;
end;
end;
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
// differing means we found a match
if (bestDifference <= 3) then
begin
Result := TFormatInformation.Create(bestFormatInfo);
exit;
end;
end;
function TFormatInformation.Equals(o: TObject): Boolean;
var
other: TFormatInformation;
begin
Result := false;
if (not(o is TFormatInformation)) then
exit;
other := TFormatInformation(o);
Result := ((FErrorCorrectionLevel = other.ErrorCorrectionLevel) and
(FDataMask = other.DataMask));
end;
function TFormatInformation.GetHashCode: Integer;
begin
Result := ((FErrorCorrectionLevel.ordinal shl 3) or FDataMask)
end;
class procedure TFormatInformation.InitializeClass;
begin
SetLength(FORMAT_INFO_DECODE_LOOKUP, $20);
FORMAT_INFO_DECODE_LOOKUP[0] := TArray<Integer>.Create($5412, 0);
FORMAT_INFO_DECODE_LOOKUP[1] := TArray<Integer>.Create($5125, 1);
FORMAT_INFO_DECODE_LOOKUP[2] := TArray<Integer>.Create($5E7C, 2);
FORMAT_INFO_DECODE_LOOKUP[3] := TArray<Integer>.Create($5B4B, 3);
FORMAT_INFO_DECODE_LOOKUP[4] := TArray<Integer>.Create($45F9, 4);
FORMAT_INFO_DECODE_LOOKUP[5] := TArray<Integer>.Create($40CE, 5);
FORMAT_INFO_DECODE_LOOKUP[6] := TArray<Integer>.Create($4F97, 6);
FORMAT_INFO_DECODE_LOOKUP[7] := TArray<Integer>.Create($4AA0, 7);
FORMAT_INFO_DECODE_LOOKUP[8] := TArray<Integer>.Create($77C4, 8);
FORMAT_INFO_DECODE_LOOKUP[9] := TArray<Integer>.Create($72F3, 9);
FORMAT_INFO_DECODE_LOOKUP[10] := TArray<Integer>.Create($7DAA, 10);
FORMAT_INFO_DECODE_LOOKUP[11] := TArray<Integer>.Create($789D, 11);
FORMAT_INFO_DECODE_LOOKUP[12] := TArray<Integer>.Create($662F, 12);
FORMAT_INFO_DECODE_LOOKUP[13] := TArray<Integer>.Create($6318, 13);
FORMAT_INFO_DECODE_LOOKUP[14] := TArray<Integer>.Create($6C41, 14);
FORMAT_INFO_DECODE_LOOKUP[15] := TArray<Integer>.Create($6976, 15);
FORMAT_INFO_DECODE_LOOKUP[$10] := TArray<Integer>.Create($1689, $10);
FORMAT_INFO_DECODE_LOOKUP[$11] := TArray<Integer>.Create($13BE, $11);
FORMAT_INFO_DECODE_LOOKUP[$12] := TArray<Integer>.Create($1CE7, $12);
FORMAT_INFO_DECODE_LOOKUP[$13] := TArray<Integer>.Create($19D0, $13);
FORMAT_INFO_DECODE_LOOKUP[20] := TArray<Integer>.Create($762, 20);
FORMAT_INFO_DECODE_LOOKUP[$15] := TArray<Integer>.Create($255, $15);
FORMAT_INFO_DECODE_LOOKUP[$16] := TArray<Integer>.Create($D0C, $16);
FORMAT_INFO_DECODE_LOOKUP[$17] := TArray<Integer>.Create($83B, $17);
FORMAT_INFO_DECODE_LOOKUP[$18] := TArray<Integer>.Create($355F, $18);
FORMAT_INFO_DECODE_LOOKUP[$19] := TArray<Integer>.Create($3068, $19);
FORMAT_INFO_DECODE_LOOKUP[$1A] := TArray<Integer>.Create($3F31, $1A);
FORMAT_INFO_DECODE_LOOKUP[$1B] := TArray<Integer>.Create($3A06, $1B);
FORMAT_INFO_DECODE_LOOKUP[$1C] := TArray<Integer>.Create($24B4, $1C);
FORMAT_INFO_DECODE_LOOKUP[$1D] := TArray<Integer>.Create($2183, $1D);
FORMAT_INFO_DECODE_LOOKUP[30] := TArray<Integer>.Create($2EDA, 30);
FORMAT_INFO_DECODE_LOOKUP[$1F] := TArray<Integer>.Create($2BED, $1F);
BITS_SET_IN_HALF_BYTE := TArray<Integer>.Create(0, 1, 1, 2, 1, 2, 2, 3, 1, 2,
2, 3, 2, 3, 3, 4);
end;
class procedure TFormatInformation.FinalizeClass;
var
i: Integer;
begin
for i := 0 to Pred(Length(FORMAT_INFO_DECODE_LOOKUP)) do
FORMAT_INFO_DECODE_LOOKUP[i] := nil;
FORMAT_INFO_DECODE_LOOKUP := nil;
BITS_SET_IN_HALF_BYTE := nil;
end;
class function TFormatInformation.numBitsDiffering(a, b: Integer): Integer;
begin
a := (a xor b); // a now has a 1 bit exactly where its bit differs with b's
// Count bits set quickly with a series of lookups:
Result := BITS_SET_IN_HALF_BYTE[(a and $0F)] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 4) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 8) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 12) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 16) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 20) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 24) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 28) and $0F];
end;
initialization
TFormatInformation.InitializeClass;
finalization
TFormatInformation.FinalizeClass;
end.
@@ -0,0 +1,248 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E.Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.Mode;
interface
uses
System.SysUtils,
ZXing.QrCode.Internal.Version;
type
/// <summary>
/// <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
/// data can be encoded to bits in the QR code standard.</p>
/// </summary>
TMode = class sealed
private
Fbits : Integer;
Fname : String;
characterCountBitsForVersions: TArray<Integer>;
class procedure InitializeClass; static;
class procedure FinalizeClass; static;
public
constructor Create(const characterCountBitsForVersions: TArray<Integer>;
const bits: Integer; const name: String); overload;
// No, we can't use an enum here. J2ME doesn't support it.
/// <summary>
///
/// </summary>
class var TERMINATOR: TMode;
/// <summary>
///
/// </summary>
class var NUMERIC: TMode;
/// <summary>
///
/// </summary>
class var ALPHANUMERIC: TMode;
/// <summary>
///
/// </summary>
class var STRUCTURED_APPEND: TMode;
/// <summary>
///
/// </summary>
class var BYTE: TMode;
/// <summary>
///
/// </summary>
class var ECI: TMode; // character counts don't apply
/// <summary>
///
/// </summary>
class var KANJI: TMode;
/// <summary>
///
/// </summary>
class var FNC1_FIRST_POSITION: TMode;
/// <summary>
///
/// </summary>
class var FNC1_SECOND_POSITION: TMode;
/// <summary>See GBT 18284-2000; "Hanzi" is a transliteration of this mode name.</summary>
class var HANZI: TMode;
/// <summary>
/// Fors the bits.
/// </summary>
/// <param name="bits">four bits encoding a QR Code data mode</param>
/// <returns>
/// <see cref="Mode"/> encoded by these bits
/// </returns>
/// <exception cref="ArgumentException">if bits do not correspond to a known mode</exception>
class function forBits(const bits: Integer): TMode; static;
/// <param name="version">version in question
/// </param>
/// <returns> number of bits used, in this QR Code symbol {@link Version}, to encode the
/// count of characters that will follow encoded in this {@link Mode}
/// </returns>
function getCharacterCountBits(const version: TVersion): Integer;
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
function ToString: String; override;
/// <summary>
/// Gets the name.
/// </summary>
property Name: String read Fname;
/// <summary>
/// Gets the bits.
/// </summary>
property Bits: Integer read Fbits;
end;
implementation
{ TMode }
constructor TMode.Create(const characterCountBitsForVersions: TArray<Integer>;
const bits: Integer; const name: String);
begin
Self.characterCountBitsForVersions := characterCountBitsForVersions;
Self.Fbits := bits;
Self.Fname := name
end;
class procedure TMode.InitializeClass;
begin
TMode.TERMINATOR := TMode.Create(TArray<Integer>.Create(0, 0, 0), 0, 'TERMINATOR');
TMode.NUMERIC := TMode.Create(TArray<Integer>.Create(10, 12, 14), 1,
'NUMERIC');
TMode.ALPHANUMERIC := TMode.Create(TArray<Integer>.Create(9, 11, 13), 2,
'ALPHANUMERIC');
TMode.STRUCTURED_APPEND := TMode.Create(TArray<Integer>.Create(0, 0, 0), 3,
'STRUCTURED_APPEND');
TMode.BYTE := TMode.Create(TArray<Integer>.Create(8, $10, $10), 4, 'BYTE');
TMode.ECI := TMode.Create(nil, 7, 'ECI');
TMode.KANJI := TMode.Create(TArray<Integer>.Create(8, 10, 12), 8, 'KANJI');
TMode.FNC1_FIRST_POSITION := TMode.Create(nil, 5, 'FNC1_FIRST_POSITION');
TMode.FNC1_SECOND_POSITION := TMode.Create(nil, 9, 'FNC1_SECOND_POSITION');
TMode.HANZI := TMode.Create(TArray<Integer>.Create(8, 10, 12), 13, 'HANZI')
end;
class procedure TMode.FinalizeClass;
begin
TMode.TERMINATOR.characterCountBitsForVersions := nil;
TMode.NUMERIC.characterCountBitsForVersions := nil;
TMode.ALPHANUMERIC.characterCountBitsForVersions := nil;
TMode.STRUCTURED_APPEND.characterCountBitsForVersions := nil;
TMode.BYTE.characterCountBitsForVersions := nil;
TMode.ECI.characterCountBitsForVersions := nil;
TMode.KANJI.characterCountBitsForVersions := nil;
TMode.FNC1_FIRST_POSITION.characterCountBitsForVersions := nil;
TMode.FNC1_SECOND_POSITION.characterCountBitsForVersions := nil;
TMode.HANZI.characterCountBitsForVersions := nil;
// SetLength(TMode.TERMINATOR.name, 0);
// SetLength(TMode.NUMERIC.name, 0);
// SetLength(TMode.ALPHANUMERIC.name, 0);
// SetLength(TMode.STRUCTURED_APPEND.name, 0);
// SetLength(TMode.BYTE.name, 0);
// SetLength(TMode.ECI.name, 0);
// SetLength(TMode.KANJI.name, 0);
// SetLength(TMode.FNC1_FIRST_POSITION.name, 0);
// SetLength(TMode.FNC1_SECOND_POSITION.name, 0);
// SetLength(TMode.HANZI.name, 0);
FreeAndNil(TMode.TERMINATOR);
FreeAndNil(TMode.NUMERIC);
FreeAndNil(TMode.ALPHANUMERIC);
FreeAndNil(TMode.STRUCTURED_APPEND);
FreeAndNil(TMode.BYTE);
FreeAndNil(TMode.ECI);
FreeAndNil(TMode.KANJI);
FreeAndNil(TMode.FNC1_FIRST_POSITION);
FreeAndNil(TMode.FNC1_SECOND_POSITION);
FreeAndNil(TMode.HANZI);
end;
class function TMode.forBits(const bits: Integer): TMode;
begin
case bits of
$0: Result := TMode.TERMINATOR;
$1: Result := TMode.NUMERIC;
$2: Result := TMode.ALPHANUMERIC;
$3: Result := TMode.STRUCTURED_APPEND;
$4: Result := TMode.BYTE;
$5: Result := TMode.FNC1_FIRST_POSITION;
$7: Result := TMode.ECI;
$8: Result := TMode.KANJI;
$9: Result := TMode.FNC1_SECOND_POSITION;
$D: Result := TMode.HANZI; // 0xD is defined in GBT 18284-2000, may not be supported in foreign country
else
raise EArgumentException.Create('Invalid arguments');
end;
end;
function TMode.getCharacterCountBits(const version: TVersion): Integer;
var
offset,
number : Integer;
begin
if (Self.characterCountBitsForVersions = nil)
then
raise EArgumentException.Create('Character count doesn''t apply to this mode');
number := version.VersionNumber;
if (number <= 9)
then
offset := 0
else
if (number <= 26)
then
offset := 1
else
offset := 2;
Result := Self.characterCountBitsForVersions[offset];
end;
function TMode.ToString: string;
begin
Result := Self.name;
end;
initialization
TMode.InitializeClass;
finalization
TMode.FinalizeClass;
end.
@@ -0,0 +1,77 @@
{
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.QRCodeDecoderMetaData;
interface
uses
ZXing.ResultPoint;
type
/// <summary>
/// Meta-data container for QR Code decoding. Instances of this class may be used to convey information back to the
/// decoding caller. Callers are expected to process this.
/// </summary>
TQRCodeDecoderMetaData = class sealed
private
Fmirrored: Boolean;
public
/// <summary>
/// Initializes a new instance of the <see cref="TQRCodeDecoderMetaData"/> class.
/// </summary>
/// <param name="mirrored">if set to <c>true</c> [mirrored].</param>
constructor Create(const mirrored: Boolean);
/// <summary>
/// Apply the result points' order correction due to mirroring.
/// </summary>
/// <param name="points">Array of points to apply mirror correction to.</param>
procedure applyMirroredCorrection(const points: TArray<IResultPoint>);
/// <summary>
/// true if the QR Code was mirrored.
/// </summary>
property IsMirrored: boolean read Fmirrored;
end;
implementation
{ TQRCodeDecoderMetaData }
constructor TQRCodeDecoderMetaData.Create(const mirrored: Boolean);
begin
Fmirrored := mirrored;
end;
procedure TQRCodeDecoderMetaData.applyMirroredCorrection(
const points: TArray<IResultPoint>);
var
bottomLeft: IResultPoint;
begin
if ((FMirrored and (points <> nil)) and (Length(points) >= 3)) then
begin
bottomLeft := points[0];
points[0] := points[2];
points[2] := bottomLeft;
// No need to 'fix' top-left and alignment pattern.
end
end;
end.
@@ -0,0 +1,681 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.Version;
interface
uses
System.SysUtils,
ZXing.Common.Bitmatrix,
ZXing.QrCode.Internal.ErrorCorrectionLevel,
ZXing.QrCode.Internal.FormatInformation,
ZXing.Common.Detector.MathUtils;
type
/// <summary>
/// See ISO 18004:2006 Annex D
/// </summary>
TVersion = class
type
TECB = class
public
count: Integer;
dataCodewords: Integer;
constructor Create(count: Integer; dataCodewords: Integer);
end;
TECBlocks = class sealed
private
ecBlocks: TArray<TECB>;
function getTotalECCodeWords: Integer;
function get_NumBlocks: Integer;
public
ecCodewordsPerBlock: Integer;
constructor Create(ecCodewordsPerBlock: Integer; ecBlocks: TArray<TECB>);
destructor Destroy; override;
function getECBlocks: TArray<TECB>;
{ property ecCodewordsPerBlock: Integer read get_ECCodewordsPerBlock;
property NumBlocks: Integer read get_NumBlocks;
property TotalECCodewords: Integer read get_TotalECCodewords;
}
property NumBlocks: Integer read get_NumBlocks;
property TotalECCodewords: Integer read getTotalECCodeWords;
end;
private
FalignmentPatternCenters: TArray<Integer>;
FecBlocks: TArray<TECBlocks>;
FTotalCodewords: Integer;
FversionNumber: Integer;
class var BuildVersions : TArray<TVersion>;
/// <summary> See ISO 18004:2006 Annex D.
/// Element i represents the raw version bits that specify version i + 7
/// </summary>
class var VERSION_DECODE_INFO: TArray<Integer>;
function CalcDimensionForVersion: Integer;
class function GetBuildVersions: TArray<TVersion>; static;
class procedure ClassInit; static;
class procedure ClassFinal; static;
public
constructor Create(const versionNumber: Integer;
const alignmentPatternCenters: TArray<Integer>;
const ecBlocks: TArray<TECBlocks>);
destructor Destroy; override;
class function decodeVersionInformation(versionBits: Integer)
: TVersion; static;
function buildFunctionPattern: TBitMatrix;
function getECBlocksForLevel(ecLevel: TErrorCorrectionLevel): TECBlocks;
class function getProvisionalVersionForDimension(
const dimension: Integer): TVersion; static;
class function getVersionForNumber(
const versionNumber: Integer): TVersion; static;
function ToString: String; override;
property DimensionForVersion: Integer read CalcDimensionForVersion;
property alignmentPatternCenters: TArray<Integer>
read FalignmentPatternCenters;
property TotalCodewords: Integer read FTotalCodewords;
property versionNumber: Integer read FversionNumber;
end;
implementation
{ TVersion.TECB }
constructor TVersion.TECB.Create(count, dataCodewords: Integer);
begin
self.count := count;
self.dataCodewords := dataCodewords;
end;
{ TVersion.TECBlocks }
constructor TVersion.TECBlocks.Create(ecCodewordsPerBlock: Integer;
ecBlocks: TArray<TECB>);
begin
self.ecBlocks := ecBlocks;
self.ecCodewordsPerBlock := ecCodewordsPerBlock;
end;
destructor TVersion.TECBlocks.Destroy;
begin
ecBlocks :=nil;
inherited;
end;
function TVersion.TECBlocks.getECBlocks: TArray<TECB>;
begin
result := self.ecBlocks;
end;
function TVersion.TECBlocks.getTotalECCodeWords: Integer;
begin
result := ecCodewordsPerBlock * NumBlocks;
end;
function TVersion.TECBlocks.get_NumBlocks: Integer;
var
total: Integer;
ecBlock: TECB;
begin
total := 0;
for ecBlock in ecBlocks do
begin
inc(total, ecBlock.count);
end;
result := total;
end;
{ TVersion }
constructor TVersion.Create(const versionNumber: Integer;
const alignmentPatternCenters: TArray<Integer>;
const ecBlocks: TArray<TECBlocks>);
var
ecBlock: TECB;
total, ecCodewords: Integer;
ecbArray: TArray<TECB>;
begin
FversionNumber := versionNumber;
FalignmentPatternCenters := alignmentPatternCenters;
FecBlocks := ecBlocks;
total := 0;
ecCodewords := ecBlocks[0].ecCodewordsPerBlock;
ecbArray := ecBlocks[0].getECBlocks;
for ecBlock in ecbArray do
Inc(total, (ecBlock.count * (ecBlock.dataCodewords + ecCodewords)));
FTotalCodewords := total;
ecbArray := nil;
end;
destructor TVersion.Destroy;
var
ecBlocks : TECBlocks;
ECB : TECB;
begin
FalignmentPatternCenters := nil;
for ecBlocks in FecBlocks do
begin
for ecb in ecBlocks.ecBlocks do
begin
ECB.Free;
//ECB := nil;
end;
ecBlocks.free;
end;
FecBlocks := nil;
inherited;
end;
function TVersion.buildFunctionPattern: TBitMatrix;
var
dimension, max, x, i, y: Integer;
begin
dimension := DimensionForVersion;
result := TBitMatrix.Create(dimension);
result.setRegion(0, 0, 9, 9);
result.setRegion((dimension - 8), 0, 8, 9);
result.setRegion(0, (dimension - 8), 9, 8);
max := Length(FalignmentPatternCenters);
x := 0;
while ((x < max)) do
begin
i := (FalignmentPatternCenters[x] - 2);
y := 0;
while ((y < max)) do
begin
if (((x <> 0) or ((y <> 0) and (y <> (max - 1)))) and
((x <> (max - 1)) or (y <> 0))) then
result.setRegion((self.alignmentPatternCenters[y] - 2), i, 5, 5);
inc(y)
end;
inc(x)
end;
result.setRegion(6, 9, 1, (dimension - $11));
result.setRegion(9, 6, (dimension - $11), 1);
if (self.versionNumber > 6) then
begin
result.setRegion((dimension - 11), 0, 3, 6);
result.setRegion(0, (dimension - 11), 6, 3)
end;
end;
class function TVersion.GetBuildVersions: TArray<TVersion>;
begin
Result := TArray<TVersion>.Create(
TVersion.Create(1, TArray<Integer>.Create(),
TArray<TECBlocks>.Create(TECBlocks.Create(7,
TArray<TECB>.Create(TECB.Create(1, 19))), TECBlocks.Create(10,
TArray<TECB>.Create(TECB.Create(1, 16))), TECBlocks.Create(13,
TArray<TECB>.Create(TECB.Create(1, 13))), TECBlocks.Create(17,
TArray<TECB>.Create(TECB.Create(1, 9))))),
TVersion.Create(2, TArray<Integer>.Create(6, 18),
TArray<TECBlocks>.Create(TECBlocks.Create(10,
TArray<TECB>.Create(TECB.Create(1, 34))), TECBlocks.Create(16,
TArray<TECB>.Create(TECB.Create(1, 28))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(1, 22))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(1, 16))))),
TVersion.Create(3, TArray<Integer>.Create(6, 22),
TArray<TECBlocks>.Create(TECBlocks.Create(15,
TArray<TECB>.Create(TECB.Create(1, 55))), TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(1, 44))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 17))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(2, 13))))),
TVersion.Create(4, TArray<Integer>.Create(6, 26),
TArray<TECBlocks>.Create(TECBlocks.Create(20,
TArray<TECB>.Create(TECB.Create(1, 80))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 32))), TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(2, 24))), TECBlocks.Create(16,
TArray<TECB>.Create(TECB.Create(4, 9))))),
TVersion.Create(5, TArray<Integer>.Create(6, 30),
TArray<TECBlocks>.Create(TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(1, 108))), TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(2, 43))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 15), TECB.Create(2, 16))),
TECBlocks.Create(22, TArray<TECB>.Create(TECB.Create(2, 11),
TECB.Create(2, 12))))),
TVersion.Create(6, TArray<Integer>.Create(6, 34),
TArray<TECBlocks>.Create(TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 68))), TECBlocks.Create(16,
TArray<TECB>.Create(TECB.Create(4, 27))), TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(4, 19))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(4, 15))))),
TVersion.Create(7, TArray<Integer>.Create(6, 22, 38),
TArray<TECBlocks>.Create(TECBlocks.Create(20,
TArray<TECB>.Create(TECB.Create(2, 78))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(4, 31))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 14), TECB.Create(4, 15))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(4, 13),
TECB.Create(1, 14))))),
TVersion.Create(8, TArray<Integer>.Create(6, 24, 42),
TArray<TECBlocks>.Create(TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(2, 97))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(2, 38), TECB.Create(2, 39))),
TECBlocks.Create(22, TArray<TECB>.Create(TECB.Create(4, 18), TECB.Create(2,
19))), TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(4, 14),
TECB.Create(2, 15))))),
TVersion.Create(9, TArray<Integer>.Create(6, 26, 46),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(2, 116))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(3, 36), TECB.Create(2, 37))),
TECBlocks.Create(20, TArray<TECB>.Create(TECB.Create(4, 16), TECB.Create(4,
17))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(4, 12),
TECB.Create(4, 13))))),
TVersion.Create(10, TArray<Integer>.Create(6, 28, 50),
TArray<TECBlocks>.Create(TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 68), TECB.Create(2, 69))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(4, 43), TECB.Create(1,
44))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(6, 19),
TECB.Create(2, 20))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(6, 15), TECB.Create(2, 16))))),
TVersion.Create(11, TArray<Integer>.Create(6, 30, 54),
TArray<TECBlocks>.Create(TECBlocks.Create(20,
TArray<TECB>.Create(TECB.Create(4, 81))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(1, 50), TECB.Create(4, 51))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(4, 22), TECB.Create(4,
23))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(3, 12),
TECB.Create(8, 13))))),
TVersion.Create(12, TArray<Integer>.Create(6, 32, 58),
TArray<TECBlocks>.Create(TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(2, 92), TECB.Create(2, 93))),
TECBlocks.Create(22, TArray<TECB>.Create(TECB.Create(6, 36), TECB.Create(2,
37))), TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(4, 20),
TECB.Create(6, 21))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(7, 14), TECB.Create(4, 15))))),
TVersion.Create(13, TArray<Integer>.Create(6, 34, 62),
TArray<TECBlocks>.Create(TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(4, 107))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(8, 37), TECB.Create(1, 38))),
TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(8, 20), TECB.Create(4,
21))), TECBlocks.Create(22, TArray<TECB>.Create(TECB.Create(12, 11),
TECB.Create(4, 12))))),
TVersion.Create(14, TArray<Integer>.Create(6, 26, 46, 66),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(3, 115), TECB.Create(1, 116))),
TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(4, 40), TECB.Create(5,
41))), TECBlocks.Create(20, TArray<TECB>.Create(TECB.Create(11, 16),
TECB.Create(5, 17))), TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(11, 12), TECB.Create(5, 13))))),
TVersion.Create(15, TArray<Integer>.Create(6, 26, 48, 70),
TArray<TECBlocks>.Create(TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(5, 87), TECB.Create(1, 88))),
TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(5, 41), TECB.Create(5,
42))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(5, 24),
TECB.Create(7, 25))), TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(11, 12), TECB.Create(7, 13))))),
TVersion.Create(16, TArray<Integer>.Create(6, 26, 50, 74),
TArray<TECBlocks>.Create(TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(5, 98), TECB.Create(1, 99))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(7, 45), TECB.Create(3,
46))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(15, 19),
TECB.Create(2, 20))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(3, 15), TECB.Create(13, 16))))),
TVersion.Create(17, TArray<Integer>.Create(6, 30, 54, 78),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(1, 107), TECB.Create(5, 108))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(10, 46), TECB.Create(1,
47))), TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(1, 22),
TECB.Create(15, 23))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(2, 14), TECB.Create(17, 15))))),
TVersion.Create(18, TArray<Integer>.Create(6, 30, 56, 82),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(5, 120), TECB.Create(1, 121))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(9, 43), TECB.Create(4,
44))), TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(17, 22),
TECB.Create(1, 23))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(2, 14), TECB.Create(19, 15))))),
TVersion.Create(19, TArray<Integer>.Create(6, 30, 58, 86),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(3, 113), TECB.Create(4, 114))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(3, 44), TECB.Create(11,
45))), TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(17, 21),
TECB.Create(4, 22))), TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(9, 13), TECB.Create(16, 14))))),
TVersion.Create(20, TArray<Integer>.Create(6, 34, 62, 90),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(3, 107), TECB.Create(5, 108))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(3, 41), TECB.Create(13,
42))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(15, 24),
TECB.Create(5, 25))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(15, 15), TECB.Create(10, 16))))),
TVersion.Create(21, TArray<Integer>.Create(6, 28, 50, 72, 94),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(4, 116), TECB.Create(4, 117))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(17, 42))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(17, 22), TECB.Create(6,
23))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(19, 16),
TECB.Create(6, 17))))),
TVersion.Create(22, TArray<Integer>.Create(6, 26, 50, 74, 98),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(2, 111), TECB.Create(7, 112))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(17, 46))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(7, 24), TECB.Create(16,
25))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(34, 13))))),
TVersion.Create(23, TArray<Integer>.Create(6, 30, 54, 78, 102),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(4, 121), TECB.Create(5, 122))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(4, 47), TECB.Create(14,
48))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(11, 24),
TECB.Create(14, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(16, 15), TECB.Create(14, 16))))),
TVersion.Create(24, TArray<Integer>.Create(6, 28, 54, 80, 106),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(6, 117), TECB.Create(4, 118))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(6, 45), TECB.Create(14,
46))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(11, 24),
TECB.Create(16, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(30, 16), TECB.Create(2, 17))))),
TVersion.Create(25, TArray<Integer>.Create(6, 32, 58, 84, 110),
TArray<TECBlocks>.Create(TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(8, 106), TECB.Create(4, 107))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(8, 47), TECB.Create(13,
48))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(7, 24),
TECB.Create(22, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(22, 15), TECB.Create(13, 16))))),
TVersion.Create(26, TArray<Integer>.Create(6, 30, 58, 86, 114),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(10, 114), TECB.Create(2, 115))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(19, 46), TECB.Create(4,
47))), TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(28, 22),
TECB.Create(6, 23))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(33, 16), TECB.Create(4, 17))))),
TVersion.Create(27, TArray<Integer>.Create(6, 34, 62, 90, 118),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(8, 122), TECB.Create(4, 123))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(22, 45), TECB.Create(3,
46))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(8, 23),
TECB.Create(26, 24))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(12, 15), TECB.Create(28, 16))))),
TVersion.Create(28, TArray<Integer>.Create(6, 26, 50, 74, 98, 122),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(3, 117), TECB.Create(10, 118))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(3, 45), TECB.Create(23,
46))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(4, 24),
TECB.Create(31, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(11, 15), TECB.Create(31, 16))))),
TVersion.Create(29, TArray<Integer>.Create(6, 30, 54, 78, 102, 126),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(7, 116), TECB.Create(7, 117))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(21, 45), TECB.Create(7,
46))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(1, 23),
TECB.Create(37, 24))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(19, 15), TECB.Create(26, 16))))),
TVersion.Create(30, TArray<Integer>.Create(6, 26, 52, 78, 104, 130),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(5, 115), TECB.Create(10, 116))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(19, 47),
TECB.Create(10, 48))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(15, 24), TECB.Create(25, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(23, 15),
TECB.Create(25, 16))))),
TVersion.Create(31, TArray<Integer>.Create(6, 30, 56, 82, 108, 134),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(13, 115), TECB.Create(3, 116))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(2, 46), TECB.Create(29,
47))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(42, 24),
TECB.Create(1, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(23, 15), TECB.Create(28, 16))))),
TVersion.Create(32, TArray<Integer>.Create(6, 34, 60, 86, 112, 138),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(17, 115))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(10, 46), TECB.Create(23, 47))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(10, 24),
TECB.Create(35, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(19, 15), TECB.Create(35, 16))))),
TVersion.Create(33, TArray<Integer>.Create(6, 30, 58, 86, 114, 142),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(17, 115), TECB.Create(1, 116))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(14, 46),
TECB.Create(21, 47))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(29, 24), TECB.Create(19, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(11, 15),
TECB.Create(46, 16))))),
TVersion.Create(34, TArray<Integer>.Create(6, 34, 62, 90, 118, 146),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(13, 115), TECB.Create(6, 116))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(14, 46),
TECB.Create(23, 47))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(44, 24), TECB.Create(7, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(59, 16),
TECB.Create(1, 17))))),
TVersion.Create(35, TArray<Integer>.Create(6, 30, 54, 78, 102, 126, 150),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(12, 121), TECB.Create(7, 122))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(12, 47),
TECB.Create(26, 48))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(39, 24), TECB.Create(14, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(22, 15),
TECB.Create(41, 16))))),
TVersion.Create(36, TArray<Integer>.Create(6, 24, 50, 76, 102, 128, 154),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(6, 121), TECB.Create(14, 122))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(6, 47), TECB.Create(34,
48))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(46, 24),
TECB.Create(10, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(2, 15), TECB.Create(64, 16))))),
TVersion.Create(37, TArray<Integer>.Create(6, 28, 54, 80, 106, 132, 158),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(17, 122), TECB.Create(4, 123))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(29, 46),
TECB.Create(14, 47))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(49, 24), TECB.Create(10, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(24, 15),
TECB.Create(46, 16))))),
TVersion.Create(38, TArray<Integer>.Create(6, 32, 58, 84, 110, 136, 162),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(4, 122), TECB.Create(18, 123))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(13, 46),
TECB.Create(32, 47))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(48, 24), TECB.Create(14, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(42, 15),
TECB.Create(32, 16))))),
TVersion.Create(39, TArray<Integer>.Create(6, 26, 54, 82, 110, 138, 166),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(20, 117), TECB.Create(4, 118))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(40, 47), TECB.Create(7,
48))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(43, 24),
TECB.Create(22, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(10, 15), TECB.Create(67, 16))))),
TVersion.Create(40, TArray<Integer>.Create(6, 30, 58, 86, 114, 142, 170),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(19, 118), TECB.Create(6, 119))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(18, 47),
TECB.Create(31, 48))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(34, 24), TECB.Create(34, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(20, 15),
TECB.Create(61, 16)))))
);
end;
function TVersion.CalcDimensionForVersion: Integer;
begin
result := 17 + 4 * versionNumber;
end;
class function TVersion.decodeVersionInformation(versionBits: Integer)
: TVersion;
var
bestDifference, bestversion, i, targetVersion, bitsDifference: Integer;
begin
bestDifference := $7FFFFFFF;
bestversion := 0;
i := 0;
while ((i < Length(TVersion.VERSION_DECODE_INFO))) do
begin
targetVersion := TVersion.VERSION_DECODE_INFO[i];
if (targetVersion = versionBits) then
begin
result := TVersion.getVersionForNumber((i + 7));
exit
end;
bitsDifference := TFormatInformation.numBitsDiffering(versionBits,
targetVersion);
if (bitsDifference < bestDifference) then
begin
bestversion := (i + 7);
bestDifference := bitsDifference
end;
inc(i)
end;
if (bestDifference <= 3) then
begin
result := TVersion.getVersionForNumber(bestversion);
exit
end;
result := nil;
end;
function TVersion.getECBlocksForLevel(ecLevel: TErrorCorrectionLevel)
: TECBlocks;
begin
result := FecBlocks[ecLevel.ordinal]
end;
class function TVersion.getProvisionalVersionForDimension(
const dimension: Integer): TVersion;
begin
Result := nil;
if ((dimension mod 4) <> 1)
then
exit;
try
Result := TVersion.getVersionForNumber(TMathUtils.Asr(dimension - $11, 2));
except
on E: EArgumentException do
Result := nil;
end
end;
class function TVersion.getVersionForNumber(
const versionNumber: Integer): TVersion;
begin
if ((versionNumber < 1) or (versionNumber > 40)) then
raise EArgumentException.Create
('version number needs to be between 1 and 40');
Result := TVersion.BuildVersions[(versionNumber - 1)];
end;
function TVersion.ToString: string;
begin
result := self.versionNumber.ToString();
end;
class procedure TVersion.ClassInit();
begin
TVersion.VERSION_DECODE_INFO := TArray<Integer>.Create($7C94, $85BC, $9A99,
$A4D3, $BBF6, $C762, $D847, $E60D, $F928, $10B78, $1145D, $12A17, $13532,
$149A6, $15683, $168C9, $177EC, $18EC4, $191E1, $1AFAB, $1B08E, $1CC1A,
$1D33F, $1ED75, $1F250, $209D5, $216F0, $228BA, $2379F, $24B0B, $2542E,
$26A64, $27541, $28C69);
TVersion.BuildVersions := TVersion.GetBuildVersions;
end;
class procedure TVersion.ClassFinal();
var version :Tversion;
begin
TVersion.VERSION_DECODE_INFO := nil;
for version in TVersion.BuildVersions do
begin
version.Free;
end;
TVersion.BuildVersions :=nil;
end;
Initialization
TVersion.ClassInit();
Finalization
TVersion.ClassFinal();
end.
@@ -0,0 +1,632 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.Detector;
interface
uses
System.SysUtils,
System.Math,
System.Generics.Defaults,
System.Generics.Collections,
ZXing.Common.BitMatrix,
ZXing.DefaultGridSampler,
ZXing.Common.DetectorResult,
ZXing.ResultPoint,
ZXing.Common.GridSampler,
ZXing.Common.Detector.MathUtils,
ZXing.Common.Detector.WhiteRectangleDetector;
type
/// <summary>
/// <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>
/// </summary>
TDataMatrixDetector = class(TObject)
private
var
Fimage: TBitMatrix;
FrectangleDetector: TWhiteRectangleDetector;
type
/// <summary>
/// Simply encapsulates two points and a number of transitions between them.
/// </summary>
TResultPointsAndTransitions = class sealed
public
From: IResultPoint;
To_: IResultPoint;
Transitions: Integer;
constructor Create(From: IResultPoint; To_: IResultPoint;
Transitions: Integer);
destructor Destroy; override;
function ToString: String; override;
end;
/// <summary>
/// Orders ResultPointsAndTransitions by number of transitions, ascending.
/// </summary>
TResultPointsAndTransitionsComparator = class sealed
(TComparer<TResultPointsAndTransitions>)
public
function Compare(const o1, o2: TResultPointsAndTransitions)
: Integer; override;
end;
var
FtransCompare: TResultPointsAndTransitionsComparator;
public
constructor Create(const image: TBitMatrix);
destructor Destroy; override;
function detect: TDetectorResult;
function transitionsBetween(Afrom, Ato: IResultPoint)
: TResultPointsAndTransitions;
function distance(a: IResultPoint; b: IResultPoint): Integer;
procedure increment(table: TDictionary<IResultPoint, Integer>;
key: IResultPoint);
function sampleGrid(image: TBitMatrix; topLeft, bottomLeft, bottomRight,
topRight: IResultPoint; dimensionX, dimensionY: Integer): TBitMatrix;
function isValid(p: IResultPoint): Boolean;
function correctTopRight(bottomLeft, bottomRight, topLeft,
topRight: IResultPoint; dimension: Integer): IResultPoint;
function correctTopRightRectangular(bottomLeft, bottomRight, topLeft,
topRight: IResultPoint; dimensionTop, dimensionRight: Integer)
: IResultPoint;
end;
implementation
{ TDataMatrixDetector }
/// <summary>
/// Initializes a new instance of the <see cref="Detector"/> class.
/// </summary>
/// <param name="image">The image.</param>
constructor TDataMatrixDetector.Create(const image: TBitMatrix);
begin
Self.Fimage := image;
Self.FrectangleDetector := TWhiteRectangleDetector.New(image);
Self.FtransCompare := TResultPointsAndTransitionsComparator.Create;
end;
destructor TDataMatrixDetector.Destroy;
begin
if (FrectangleDetector <> nil) then
FrectangleDetector.Free;
FtransCompare.Free;
inherited;
end;
/// <summary>
/// <p>Detects a Data Matrix Code in an image.</p>
/// </summary>
/// <returns><see cref="DetectorResult" />encapsulating results of detecting a Data Matrix Code or null</returns>
function TDataMatrixDetector.detect(): TDetectorResult;
var
topRight, correctedTopRight, pointA, pointB, pointC, pointD, maybeTopLeft,
bottomLeft, bottomRight, topLeft, maybeBottomRight, point: IResultPoint;
cornerPoints: TArray<IResultPoint>;
bits: TBitMatrix;
entry: TPair<IResultPoint, Integer>; // TKeyValuePair
Transitions: TObjectList<TResultPointsAndTransitions>;
lSideOne, lSideTwo, transBetween, transA, transB: TResultPointsAndTransitions;
pointCount: TDictionary<IResultPoint, Integer>;
corners: TArray<IResultPoint>;
dimensionTop, dimensionRight, dimension, dimensionCorrected: Integer;
begin
Result := nil;
if (FrectangleDetector = nil) then
// can be null, if the image is to small
exit;
cornerPoints := FrectangleDetector.detect();
if (cornerPoints = nil) then
exit;
pointA := cornerPoints[0];
pointB := cornerPoints[1];
pointC := cornerPoints[2];
pointD := cornerPoints[3];
// 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
Transitions := TObjectList<TResultPointsAndTransitions>.Create;
pointCount := TDictionary<IResultPoint, Integer>.Create();
try
Transitions.Add(transitionsBetween(pointA, pointB));
Transitions.Add(transitionsBetween(pointA, pointC));
Transitions.Add(transitionsBetween(pointB, pointD));
Transitions.Add(transitionsBetween(pointC, pointD));
Transitions.Sort(FtransCompare);
// Sort by number of transitions. First two will be the two solid sides; last two
// will be the two alternating black/white sides
lSideOne := Transitions[0];
lSideTwo := 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.
increment(pointCount, lSideOne.From);
increment(pointCount, lSideOne.To_);
increment(pointCount, lSideTwo.From);
increment(pointCount, lSideTwo.To_);
maybeTopLeft := nil;
bottomLeft := nil;
maybeBottomRight := nil;
for entry in pointCount do
begin
point := entry.key;
if (entry.Value = 2) then
bottomLeft := point
// this is definitely the bottom left, then -- end of two L sides
else
begin
// Otherwise it's either top left or bottom right -- just assign the two arbitrarily now
if (maybeTopLeft = nil) then
maybeTopLeft := point
else
maybeBottomRight := point;
end;
end;
if ((maybeTopLeft = nil) or (bottomLeft = nil) or (maybeBottomRight = nil))
then
exit;
// Bottom left is correct but top left and bottom right might be switched
corners := TArray<IResultPoint>.Create(maybeTopLeft, bottomLeft,
maybeBottomRight);
// Use the dot product trick to sort them out
TResultPointHelpers.orderBestPatterns(corners);
// Now we know which is which:
bottomRight := corners[0];
bottomLeft := corners[1];
topLeft := corners[2];
// Which point didn't we find in relation to the "L" sides? that's the top right corner
if (not pointCount.ContainsKey(pointA)) then
topRight := pointA
else if (not pointCount.ContainsKey(pointB)) then
topRight := pointB
else if (not pointCount.ContainsKey(pointC)) then
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.
transBetween := transitionsBetween(topLeft, topRight);
dimensionTop := transBetween.Transitions;
transBetween.Free;
transBetween := transitionsBetween(bottomRight, topRight);
dimensionRight := transBetween.Transitions;
transBetween.Free;
if ((dimensionTop and $01) = 1) then
// it can't be odd, so, round... up?
Inc(dimensionTop);
Inc(dimensionTop, 2);
if ((dimensionRight and $01) = 1) then
// it can't be odd, so, round... up?
Inc(dimensionRight);
Inc(dimensionRight, 2);
// Rectangular symbols are 6x16, 6x28, 10x24, 10x32, 14x32, or 14x44. If one dimension is more
// than twice the other, it's certainly rectangular, but to cut a bit more slack we accept it as
// rectangular if the bigger side is at least 7/4 times the other:
if (((4 * dimensionTop) >= (7 * dimensionRight)) or
((4 * dimensionRight) >= (7 * dimensionTop))) then
begin
// The matrix is rectangular
correctedTopRight := correctTopRightRectangular(bottomLeft, bottomRight,
topLeft, topRight, dimensionTop, dimensionRight);
if (correctedTopRight = nil) then
correctedTopRight := topRight;
transBetween := transitionsBetween(topLeft, correctedTopRight);
dimensionTop := transBetween.Transitions;
transBetween.Free;
transBetween := transitionsBetween(bottomRight, correctedTopRight);
dimensionRight := transBetween.Transitions;
transBetween.Free;
if ((dimensionTop and $01) = 1) then
// it can't be odd, so, round... up?
Inc(dimensionTop);
if ((dimensionRight and $01) = 1) then
// it can't be odd, so, round... up?
Inc(dimensionRight);
bits := sampleGrid(Fimage, topLeft, bottomLeft, bottomRight,
correctedTopRight, dimensionTop, dimensionRight)
end
else
begin
// The matrix is square
dimension := System.Math.Min(dimensionRight, dimensionTop);
// correct top right point to match the white module
correctedTopRight := correctTopRight(bottomLeft, bottomRight, topLeft,
topRight, dimension);
if (correctedTopRight = nil) then
correctedTopRight := topRight;
// Redetermine the dimension using the corrected top right point
transA := transitionsBetween(topLeft, correctedTopRight);
transB := transitionsBetween(bottomRight, correctedTopRight);
dimensionCorrected :=
(System.Math.Max(transA.Transitions, transB.Transitions) + 1);
transA.Free;
transB.Free;
if ((dimensionCorrected and $01) = 1) then
Inc(dimensionCorrected);
bits := sampleGrid(Fimage, topLeft, bottomLeft, bottomRight,
correctedTopRight, dimensionCorrected, dimensionCorrected);
end;
if (bits = nil) then
exit;
Result := TDetectorResult.Create(bits, TArray<IResultPoint>.Create(topLeft,
bottomLeft, bottomRight, correctedTopRight));
finally
pointCount.Free;
Transitions.Free;
cornerPoints := nil;
end;
end;
/// <summary>
/// Calculates the position of the white top right module using the output of the rectangle detector
/// for a rectangular matrix
/// </summary>
function TDataMatrixDetector.correctTopRightRectangular(bottomLeft, bottomRight,
topLeft, topRight: IResultPoint; dimensionTop, dimensionRight: Integer)
: IResultPoint;
var
corr, norm, cos, sin: Single;
c1, c2: IResultPoint;
l1, l2: Integer;
rpT1, rpT2: TResultPointsAndTransitions;
begin
corr := (distance(bottomLeft, bottomRight) / dimensionTop);
norm := distance(topLeft, topRight);
if (norm = 0) then
begin
Result := nil;
exit
end;
cos := ((topRight.X - topLeft.X) / norm);
sin := ((topRight.Y - topLeft.Y) / norm);
c1 := TResultPointHelpers.CreateResultPoint((topRight.X + (corr * cos)),
(topRight.Y + (corr * sin)));
corr := (distance(bottomLeft, topLeft) / dimensionRight);
norm := distance(bottomRight, topRight);
if (norm = 0) then
begin
Result := nil;
exit;
end;
cos := ((topRight.X - bottomRight.X) / norm);
sin := ((topRight.Y - bottomRight.Y) / norm);
c2 := TResultPointHelpers.CreateResultPoint((topRight.X + (corr * cos)),
(topRight.Y + (corr * sin)));
if (not isValid(c1)) then
begin
if (isValid(c2)) then
begin
Result := c2;
exit;
end;
Result := nil;
exit;
end;
if (not isValid(c2)) then
begin
Result := c1;
exit;
end;
rpT1 := transitionsBetween(topLeft, c1);
rpT2 := transitionsBetween(bottomRight, c1);
l1 := (Abs((dimensionTop - rpT1.Transitions)) +
Abs((dimensionRight - rpT2.Transitions)));
FreeAndNil(rpT1);
FreeAndNil(rpT2);
rpT1 := transitionsBetween(topLeft, c2);
rpT2 := transitionsBetween(bottomRight, c2);
l2 := (Abs((dimensionTop - rpT1.Transitions)) +
Abs((dimensionRight - rpT2.Transitions)));
FreeAndNil(rpT1);
FreeAndNil(rpT2);
if (l1 <= l2) then
begin
Result := c1;
end
else
begin
Result := c2;
end;
end;
/// <summary>
/// Calculates the position of the white top right module using the output of the rectangle detector
/// for a square matrix
/// </summary>
function TDataMatrixDetector.correctTopRight(bottomLeft, bottomRight, topLeft,
topRight: IResultPoint; dimension: Integer): IResultPoint;
var
corr, cos, sin: Single;
norm: Integer;
c1, c2: IResultPoint;
l1, l2: Integer;
transA, transB: TResultPointsAndTransitions;
begin
corr := (distance(bottomLeft, bottomRight) / dimension);
norm := distance(topLeft, topRight);
if (norm = 0) then
begin
Result := nil;
exit;
end;
cos := ((topRight.X - topLeft.X) / norm);
sin := ((topRight.Y - topLeft.Y) / norm);
c1 := TResultPointHelpers.CreateResultPoint((topRight.X + (corr * cos)),
(topRight.Y + (corr * sin)));
corr := (distance(bottomLeft, topLeft) / dimension);
norm := distance(bottomRight, topRight);
if (norm = 0) then
begin
Result := nil;
exit;
end;
cos := ((topRight.X - bottomRight.X) / norm);
sin := ((topRight.Y - bottomRight.Y) / norm);
c2 := TResultPointHelpers.CreateResultPoint((topRight.X + (corr * cos)),
(topRight.Y + (corr * sin)));
if (not isValid(c1)) then
begin
if (isValid(c2)) then
begin
Result := c2;
exit;
end;
Result := nil;
exit;
end;
if (not isValid(c2)) then
begin
Result := c1;
exit;
end;
transA := transitionsBetween(topLeft, c1);
transB := transitionsBetween(bottomRight, c1);
l1 := (Abs(transA.Transitions - transB.Transitions));
transA.Free;
transB.Free;
transA := transitionsBetween(topLeft, c2);
transB := transitionsBetween(bottomRight, c2);
l2 := (Abs(transA.Transitions - transB.Transitions));
transA.Free;
transB.Free;
if (l1 <= l2) then
begin
Result := c1;
end
else
begin
Result := c2;
end;
end;
function TDataMatrixDetector.isValid(p: IResultPoint): Boolean;
begin
Result := ((((p.X >= 0) and (p.X < Fimage.Width)) and (p.Y > 0)) and
(p.Y < Fimage.Height))
end;
// L2 distance
function TDataMatrixDetector.distance(a: IResultPoint; b: IResultPoint)
: Integer;
begin
Result := TMathUtils.round(TResultPointHelpers.distance(a, b))
end;
/// <summary>
/// Increments the Integer associated with a key by one.
/// </summary>
procedure TDataMatrixDetector.increment
(table: TDictionary<IResultPoint, Integer>; key: IResultPoint);
var
Value: Integer;
begin
if (table.ContainsKey(key)) then
begin
Value := table[key];
table[key] := (Value + 1);
end
else
table.Add(key, 1); // table[key] := 1;
end;
function TDataMatrixDetector.sampleGrid(image: TBitMatrix;
topLeft, bottomLeft, bottomRight, topRight: IResultPoint;
dimensionX, dimensionY: Integer): TBitMatrix;
begin
// TGridSampler.instance
Result := TDefaultGridSampler.sampleGrid(image, dimensionX, dimensionY, 0.5,
0.5, (dimensionX - 0.5), 0.5, (dimensionX - 0.5), (dimensionY - 0.5), 0.5,
(dimensionY - 0.5), topLeft.X, topLeft.Y, topRight.X, topRight.Y,
bottomRight.X, bottomRight.Y, bottomLeft.X, bottomLeft.Y);
end;
/// <summary>
/// Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
/// </summary>
function TDataMatrixDetector.transitionsBetween(Afrom, Ato: IResultPoint)
: TResultPointsAndTransitions;
var
temp, fromX, fromY, toX, toY: Integer;
steep: Boolean;
dx, dy: Int64;
xstep, ystep, Transitions: Integer;
error: Int64;
inBlack, isBlack: Boolean;
X, Y: Integer;
begin
// See QR Code Detector, sizeOfBlackWhiteBlackRun()
fromX := Trunc(Afrom.X);
fromY := Trunc(Afrom.Y);
toX := Trunc(Ato.X);
toY := Trunc(Ato.Y);
steep := (Abs((toY - fromY)) > Abs((toX - fromX)));
if (steep) then
begin
temp := fromX;
fromX := fromY;
fromY := temp;
temp := toX;
toX := toY;
toY := temp;
end;
dx := Abs(toX - fromX);
dy := Abs(toY - fromY);
error := TMathUtils.Asr(-dx, 1);
if (fromY < toY) then
ystep := 1
else
ystep := -1;
if (fromX < toX) then
xstep := 1
else
xstep := -1;
Transitions := 0;
if steep then
inBlack := Fimage[fromY, fromX]
else
inBlack := Fimage[fromX, fromY];
X := fromX;
Y := fromY;
while ((X <> toX)) do
begin
if steep then
isBlack := Fimage[Y, X]
else
isBlack := Fimage[X, Y];
if (isBlack <> inBlack) then
begin
Inc(Transitions);
inBlack := isBlack;
end;
Inc(error, dy);
if (error > 0) then
begin
if (Y = toY) then
break;
Inc(Y, ystep);
Dec(error, dx);
end;
Inc(X, xstep)
end;
Result := TResultPointsAndTransitions.Create(Afrom, Ato, Transitions);
end;
{ TResultPointsAndTransitions }
constructor TDataMatrixDetector.TResultPointsAndTransitions.Create
(From: IResultPoint; To_: IResultPoint; Transitions: Integer);
begin
Self.From := From;
Self.To_ := To_;
Self.Transitions := Transitions;
end;
destructor TDataMatrixDetector.TResultPointsAndTransitions.Destroy;
begin
// if (Assigned(Self.From)) then
// Self.From := nil;
//
// if (Assigned(Self.To_)) then
// Self.To_:=nil;
inherited;
end;
function TDataMatrixDetector.TResultPointsAndTransitions.ToString: string;
begin
Result := From.ToString + '/' + To_.ToString + '/' + IntToStr(Transitions);
end;
function TDataMatrixDetector.TResultPointsAndTransitionsComparator.Compare
(const o1, o2: TResultPointsAndTransitions): Integer;
begin
Result := (o1.Transitions - o2.Transitions);
end;
end.
@@ -0,0 +1,69 @@
{
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E.Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.AlignmentPattern;
interface
uses ZXing.ResultPoint;
type
/// <summary>
/// As we did for ResultPoint, we use an interfaced object to implement automatic deallocation
/// of TAligmnentPattern instances
/// the actual implementation of this interface is in unit ZXing.QrCode.Internal.AlignmentPatternImplementation
/// </summary>
IAlignmentPattern = interface(IResultPoint)
['{8C0A5D01-9620-42B6-BE3E-9C40717B5B38}']
/// <summary> <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
/// position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
/// </summary>
function aboutEquals(const moduleSize, i, j: Single): Boolean;
/// <summary>
/// Combines this object's current estimate of a finder pattern position and module size
/// with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.
/// </summary>
/// <param name="i">The i.</param>
/// <param name="j">The j.</param>
/// <param name="newModuleSize">New size of the module.</param>
/// <returns></returns>
function combineEstimate(const i, j, newModuleSize: Single): IAlignmentPattern;
end;
/// <summary>
/// contains all static methods for using IAlignmentPatterns instances
/// </summary>
TAlignmentPatternHelpers = class
/// <summary>
/// IAlignmentPattern instances must be obtained by calling this function
/// </summary>
class function CreateAlignmentPattern(const posX, posY, estimatedModuleSize: Single):IAlignmentPattern;
end;
implementation
uses ZXing.QrCode.Internal.AlignmentPatternImplementation;
{ TAlignmentPatternHelpers }
class function TAlignmentPatternHelpers.CreateAlignmentPattern(const posX, posY, estimatedModuleSize: Single):IAlignmentPattern;
begin
result := ZXing.QrCode.Internal.AlignmentPatternImplementation.NewAlignmentPattern(posX,posY,estimatedModuleSize);
end;
end.
@@ -0,0 +1,407 @@
{
* 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.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E.Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.AlignmentPatternFinder;
interface
uses
System.SysUtils,
System.Math,
System.Generics.Collections,
ZXing.Common.BitMatrix,
ZXing.QrCode.Internal.AlignmentPattern, // nullableType,
ZXing.ResultPoint,
ZXing.Common.Detector.MathUtils;
type
/// <summary> <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
/// patterns but are smaller and appear at regular intervals throughout the image.</p>
///
/// <p>At the moment this only looks for the bottom-right alignment pattern.</p>
///
/// <p>This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,
/// pasted and stripped down here for maximum performance but does unfortunately duplicate
/// some code.</p>
///
/// <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.</p>
///
/// </summary>
TAlignmentPatternFinder = class sealed
private
image: TBitMatrix;
possibleCenters: TList<IAlignmentPattern>;
startX, startY: Integer;
width, height: Integer;
moduleSize: Single;
crossCheckStateCount: TArray<Integer>;
resultPointCallback: TResultPointCallback;
/// <summary> Given a count of black/white/black pixels just seen and an end position,
/// figures the location of the center of this black/white/black run.
/// </summary>
class function centerFromEnd(const stateCount: TArray<Integer>;
const pEnd: Integer): Single; static;
/// <param name="stateCount">count of black/white/black pixels just read
/// </param>
/// <returns> true iff the proportions of the counts is close enough to the 1/1/1 ratios
/// used by alignment patterns to be considered a match
/// </returns>
function foundPatternCross(const stateCount: TArray<Integer>): Boolean;
/// <summary>
/// <p>After a horizontal scan finds a potential alignment pattern, this method
/// "cross-checks" by scanning down vertically through the center of the possible
/// alignment pattern to see if the same proportion is detected.</p>
/// </summary>
/// <param name="startI">row where an alignment pattern was detected</param>
/// <param name="centerJ">center of the section that appears to cross an alignment pattern</param>
/// <param name="maxCount">maximum reasonable number of modules that should be
/// observed in any reading state, based on the results of the horizontal scan</param>
/// <param name="originalStateCountTotal">The original state count total.</param>
/// <returns>
/// vertical center of alignment pattern, or null if not found
/// </returns>
function crossCheckVertical(const startI, centerJ, maxCount,
originalStateCountTotal: Integer): Single;
/// <summary> <p>This is called when a horizontal scan finds a possible alignment pattern. It will
/// cross check with a vertical scan, and if successful, will see if this pattern had been
/// found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
/// found the alignment pattern.</p>
///
/// </summary>
/// <param name="stateCount">reading state module counts from horizontal scan
/// </param>
/// <param name="i">row where alignment pattern may be found
/// </param>
/// <param name="j">end of possible alignment pattern in row
/// </param>
/// <returns> {@link TAlignmentPattern} if we have found the same pattern twice, or null if not
/// </returns>
function handlePossibleCenter(const stateCount: TArray<Integer>;
const i, j: Integer): IAlignmentPattern;
public
/// <summary> <p>Creates a finder that will look in a portion of the whole image.</p>
///
/// </summary>
/// <param name="image">image to search
/// </param>
/// <param name="startX">left column from which to start searching
/// </param>
/// <param name="startY">top row from which to start searching
/// </param>
/// <param name="width">width of region to search
/// </param>
/// <param name="height">height of region to search
/// </param>
/// <param name="moduleSize">estimated module size so far
/// </param>
constructor Create(const image: TBitMatrix; const startX, startY,
width, height: Integer; const moduleSize: Single;
resultPointCallback: TResultPointCallback);
destructor Destroy; override;
/// <summary> <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
/// it's pretty performance-critical and so is written to be fast foremost.</p>
///
/// </summary>
/// <returns> {@link TAlignmentPattern} if found
/// </returns>
function find: IAlignmentPattern;
end;
implementation
{ TAlignmentPatternFinder }
constructor TAlignmentPatternFinder.Create(const image: TBitMatrix;
const startX, startY, width, height: Integer; const moduleSize: Single;
resultPointCallback: TResultPointCallback);
begin
Self.image := image;
Self.possibleCenters := TList<IAlignmentPattern>.Create;
Self.possibleCenters.Capacity := 5;
Self.startX := startX;
Self.startY := startY;
Self.width := width;
Self.height := height;
Self.moduleSize := moduleSize;
Self.crossCheckStateCount := TArray<Integer>.Create(0, 0, 0);
Self.resultPointCallback := resultPointCallback;
end;
destructor TAlignmentPatternFinder.Destroy;
begin
if possibleCenters<>nil then
possibleCenters.Clear;
FreeAndNil(self.possibleCenters);
self.crossCheckStateCount := nil;
self.resultPointCallback :=nil;
inherited;
end;
function TAlignmentPatternFinder.find: IAlignmentPattern;
var
confirmed: IAlignmentPattern;
maxJ, middleI, iGen, i, j, currentState: Integer;
stateCount: TArray<Integer>;
begin
startX := Self.startX;
height := Self.height;
maxJ := startX + self.width;
middleI := Self.startY + (TMathUtils.Asr(height, 1));
// We are looking for black/white/black modules in 1:1:1 ratio;
// this tracks the number of black/white/black modules seen so far
stateCount := TArray<Integer>.Create(0, 0, 0);
for iGen := 0 to Pred(height) do
begin
// Search from middle outwards
if ((iGen and 1) = 0)
then
i := middleI + (TMathUtils.Asr(iGen + 1, 1))
else
i := middleI + (-1 * TMathUtils.Asr(iGen + 1, 1));
stateCount[0] := 0;
stateCount[1] := 0;
stateCount[2] := 0;
j := startX;
// Burn off leading white pixels before anything else; if we start in the middle of
// a white run, it doesn't make sense to count its length, since we don't know if the
// white run continued to the left of the start point
while ((j < maxJ) and (not Self.image[j, i])) do
Inc(j);
currentState := 0;
while ((j < maxJ)) do
begin
if (Self.image[j, i]) then
begin
// Black pixel
if (currentState = 1) then
begin
// Counting black pixels
Inc(stateCount[currentState]);
end else
begin
// Counting white pixels
if (currentState = 2) then
begin
// A winner?
if (Self.foundPatternCross(stateCount)) then
begin
// Yes
confirmed := self.handlePossibleCenter(stateCount, i, j);
if (confirmed <> nil) then
begin
result := confirmed;
exit
end;
end;
stateCount[0] := stateCount[2];
stateCount[1] := 1;
stateCount[2] := 0;
currentState := 1;
end else
begin
Inc(currentState);
Inc(stateCount[currentState]);
end;
end;
end else
begin
// White pixel
if (currentState = 1)
then
// Counting black pixels
Inc(currentState);
Inc(stateCount[currentState])
end;
Inc(j)
end;
if (Self.foundPatternCross(stateCount)) then
begin
confirmed := self.handlePossibleCenter(stateCount, i, maxJ);
if (confirmed <> nil) then
begin
result := confirmed;
exit;
end;
end;
end;
// Hmm, nothing we saw was observed and confirmed twice. If we had
// any guess at all, return it.
if (Self.possibleCenters.Count <> 0)
then
Result := Self.possibleCenters[0]
else
Result := nil;
end;
class function TAlignmentPatternFinder.centerFromEnd(
const stateCount: TArray<Integer>; const pEnd: Integer): Single;
begin
Result := (pEnd - stateCount[2]) - (stateCount[1] / 2.0);
if (Single.IsNaN(Result))
then
Result := -1;
end;
function TAlignmentPatternFinder.foundPatternCross(
const stateCount: TArray<Integer>): Boolean;
var
maxVariance: Single;
i: Integer;
begin
maxVariance := moduleSize / 2;
for i := 0 to Pred(3) do
begin
if (Abs(self.moduleSize - stateCount[i]) >= maxVariance) then
begin
Result := false;
exit;
end;
end;
Result := true;
end;
function TAlignmentPatternFinder.crossCheckVertical(const startI, centerJ,
maxCount, originalStateCountTotal: Integer): Single;
var
maxI,
i, stateCountTotal: Integer;
stateCount: TArray<Integer>;
begin
maxI := Self.image.height;
stateCount := Self.crossCheckStateCount;
stateCount[0] := 0;
stateCount[1] := 0;
stateCount[2] := 0;
// Start counting up from center
i := startI;
while (((i >= 0) and self.image[centerJ, i]) and
(stateCount[1] <= maxCount)) do
begin
Inc(stateCount[1]);
Dec(i);
end;
// If already too many modules in this state or ran off the edge:
if ((i < 0) or (stateCount[1] > maxCount)) then
begin
Result := -1;
exit;
end;
while (((i >= 0) and (not Self.image[centerJ, i])) and
(stateCount[0] <= maxCount)) do
begin
Inc(stateCount[0]);
Dec(i);
end;
if (stateCount[0] > maxCount) then
begin
Result := -1;
exit;
end;
// Now also count down from center
i := (startI + 1);
while (((i < maxI) and Self.image[centerJ, i]) and
(stateCount[1] <= maxCount)) do
begin
Inc(stateCount[1]);
Inc(i);
end;
if ((i = maxI) or (stateCount[1] > maxCount)) then
begin
Result := -1;
exit;
end;
while (((i < maxI) and not self.image[centerJ, i]) and
(stateCount[2] <= maxCount)) do
begin
Inc(stateCount[2]);
Inc(i);
end;
if (stateCount[2] > maxCount) then
begin
Result := -1;
exit;
end;
stateCountTotal := (stateCount[0] + stateCount[1] + stateCount[2]);
if ((5 * Abs(stateCountTotal - originalStateCountTotal)) >=
(2 * originalStateCountTotal)) then
begin
Result := -1;
exit;
end;
if Self.foundPatternCross(stateCount)
then
Result := TAlignmentPatternFinder.centerFromEnd(stateCount, i)
else
Result := -1;
end;
function TAlignmentPatternFinder.handlePossibleCenter(
const stateCount: TArray<Integer>; const i, j: Integer): IAlignmentPattern;
var
center, point: IAlignmentPattern;
stateCountTotal: Integer;
estimatedModuleSize: Single;
centerJ, centerI: Single;
begin
stateCountTotal := (stateCount[0] + stateCount[1] + stateCount[2]);
centerJ := TAlignmentPatternFinder.centerFromEnd(stateCount, j);
if (centerJ = -1) then
begin
Result := nil;
exit;
end;
centerI := crossCheckVertical(i, Floor(centerJ), (2 * stateCount[1]),
stateCountTotal);
if (centerI <> -1) then
begin
estimatedModuleSize := (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0;
for center in Self.possibleCenters do
begin
// Look for about the same center and module size:
if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) then
begin
Result := center.combineEstimate(centerI, centerJ, estimatedModuleSize);
exit;
end;
end;
// Hadn't found this before; save it
point := TAlignmentPatternHelpers.CreateAlignmentPattern(centerJ, centerI, estimatedModuleSize);
Self.possibleCenters.Add(point);
if Assigned(resultPointCallback)
then
Self.resultPointCallback(point);
end;
Result := nil;
end;
end.
@@ -0,0 +1,112 @@
{
* 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.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E.Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.AlignmentPatternImplementation;
interface
uses
ZXing.QrCode.Internal.AlignmentPattern,
ZXing.ResultPoint,
ZXing.ResultPointImplementation;
// this is the function that actually creates IAlignmentPattern instances.
// the actual implementation of the interface is totally hidden to all other units,
// since even its declaration is in the implementation section of this unit
function NewAlignmentPattern(const posX, posY, estimatedModuleSize: Single):IAlignmentPattern;
implementation
{ TAlignmentPattern }
type
/// <summary> <p>Encapsulates an alignment pattern, which are the smaller square patterns found in
/// all but the simplest QR Codes.</p>
///
/// </summary>
TAlignmentPattern = class sealed(TResultPoint,IResultPoint,IAlignmentPattern)
private
estimatedModuleSize: Single;
constructor Create(const posX, posY, estimatedModuleSize: Single);
/// <summary> <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
/// position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
/// </summary>
function aboutEquals(const moduleSize, i, j: Single): Boolean;
/// <summary>
/// Combines this object's current estimate of a finder pattern position and module size
/// with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.
/// </summary>
/// <param name="i">The i.</param>
/// <param name="j">The j.</param>
/// <param name="newModuleSize">New size of the module.</param>
/// <returns></returns>
function combineEstimate(const i, j,
newModuleSize: Single): IAlignmentPattern;
end;
constructor TAlignmentPattern.Create(const posX, posY,
estimatedModuleSize: Single);
begin
inherited Create(posX, posY);
Self.estimatedModuleSize := estimatedModuleSize;
end;
function TAlignmentPattern.aboutEquals(const moduleSize, i, j: Single): Boolean;
var
moduleSizeDiff: Single;
begin
if ((Abs(i - Self.y) <= moduleSize) and
(Abs(j - Self.x) <= moduleSize)) then
begin
moduleSizeDiff := Abs(moduleSize - self.estimatedModuleSize);
Result := ((moduleSizeDiff <= 1) or
(moduleSizeDiff <= self.estimatedModuleSize));
end else Result := false;
end;
function TAlignmentPattern.combineEstimate(const i, j,
newModuleSize: Single): IAlignmentPattern;
var
combinedX,
combinedY,
combinedModuleSize : Single;
begin
combinedX := (self.x + j) / 2.0;
combinedY := (self.y + i) / 2.0;
combinedModuleSize := (estimatedModuleSize + newModuleSize) / 2.0;
Result := TAlignmentPattern.Create(combinedX, combinedY, combinedModuleSize);
end;
function NewAlignmentPattern(const posX, posY, estimatedModuleSize: Single):IAlignmentPattern;
begin
result := TAlignmentPattern.Create(posX,posY,estimatedModuleSize);
end;
end.
@@ -0,0 +1,492 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.Detector;
interface
uses
System.SysUtils,
System.Math,
Generics.Collections,
ZXing.Common.BitMatrix,
ZXing.ResultPoint,
ZXing.Common.DetectorResult,
ZXing.DecodeHintType,
ZXing.Common.PerspectiveTransform,
ZXing.QrCode.Internal.FinderPatternInfo,
ZXing.QrCode.Internal.FinderPatternFinder,
ZXing.QrCode.Internal.FinderPattern,
ZXing.QrCode.Internal.AlignmentPattern,
ZXing.QrCode.Internal.AlignmentPatternFinder,
ZXing.QrCode.Internal.Version,
ZXing.DefaultGridSampler,
ZXing.Common.Detector.MathUtils;
type
/// <summary>
/// <p>Encapsulates logic that can detect a QR Code in an image, even if the QR Code
/// is rotated or skewed, or partially obscured.</p>
/// </summary>
TDetector = class
private
FImage: TBitMatrix;
FResultPointCallback: TResultPointCallback;
function calculateModuleSizeOneWay(pattern: IResultpoint;
otherPattern: IResultpoint): Single;
class function computeDimension(topLeft: IResultpoint;
topRight: IResultpoint; bottomLeft: IResultpoint; moduleSize: Single;
var dimension: Integer): boolean; static;
class function createTransform(const topLeft, topRight, bottomLeft,
AlignmentPattern: IResultpoint; const dimension: Integer)
: TPerspectiveTransform; static;
class function sampleGrid(image: TBitMatrix;
transform: TPerspectiveTransform; dimension: Integer): TBitMatrix; static;
function sizeOfBlackWhiteBlackRun(fromX: Integer; fromY: Integer;
toX: Integer; toY: Integer): Single;
function sizeOfBlackWhiteBlackRunBothWays(fromX: Integer; fromY: Integer;
toX: Integer; toY: Integer): Single;
protected
function calculateModuleSize(const topLeft, topRight,
bottomLeft: IResultPoint): Single; virtual;
function findAlignmentInRegion(const overallEstModuleSize: Single;
const estAlignmentX, estAlignmentY: Integer;
const allowanceFactor: Single): IAlignmentPattern;
/// <summary>
/// Processes the finder pattern info.
/// </summary>
/// <param name="info">The info.</param>
/// <returns></returns>
function processFinderPatternInfo(const info: TFinderPatternInfo)
: TDetectorResult; virtual;
public
/// <summary>
/// Initializes a new instance of the <see cref="Detector"/> class.
/// </summary>
/// <param name="image">The image.</param>
constructor Create(const image: TBitMatrix);
destructor Destroy; override;
/// <summary>
/// <p>Detects a QR Code in an image.</p>
/// </summary>
/// <returns>
/// <see cref="TDetectorResult"/> encapsulating results of detecting a QR Code
/// </returns>
function detect(): TDetectorResult; overload;
/// <summary>
/// <p>Detects a QR Code in an image.</p>
/// </summary>
/// <param name="hints">optional hints to detector</param>
/// <returns>
/// <see cref="TDetectorResult"/> encapsulating results of detecting a QR Code
/// </returns>
function detect(const hints: TDictionary<TDecodeHintType, TObject>)
: TDetectorResult; overload;
property image: TBitMatrix read FImage;
property ResultPointCallback: TResultPointCallback
read FResultPointCallback;
end;
implementation
{ TDetector }
constructor TDetector.Create(const image: TBitMatrix);
begin
FImage := image;
end;
destructor TDetector.Destroy;
begin
FResultPointCallback := nil;
inherited;
end;
function TDetector.calculateModuleSize(const topLeft, topRight,
bottomLeft: IResultPoint): Single;
begin
Result := ((self.calculateModuleSizeOneWay(topLeft, topRight) +
self.calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2)
end;
function TDetector.calculateModuleSizeOneWay(pattern: IResultPoint;
otherPattern: IResultPoint): Single;
var
moduleSizeEst1, moduleSizeEst2: Single;
begin
moduleSizeEst1 := self.sizeOfBlackWhiteBlackRunBothWays(Floor(pattern.X),
Floor(pattern.Y), Floor(otherPattern.X), Floor(otherPattern.Y));
moduleSizeEst2 := self.sizeOfBlackWhiteBlackRunBothWays(Floor(otherPattern.X),
Floor(otherPattern.Y), Floor(pattern.X), Floor(pattern.Y));
if (moduleSizeEst1 = -1) then
begin
Result := (moduleSizeEst2 / 7);
exit
end;
if (moduleSizeEst2 = -1) then
begin
Result := (moduleSizeEst1 / 7);
exit
end;
Result := ((moduleSizeEst1 + moduleSizeEst2) / 14);
end;
class function TDetector.computeDimension(topLeft: IResultPoint;
topRight: IResultPoint; bottomLeft: IResultPoint; moduleSize: Single;
var dimension: Integer): boolean;
var
tltrCentersDimension, tlblCentersDimension: Integer;
begin
tltrCentersDimension :=
round((TResultPointHelpers.distance(topLeft, topRight) / moduleSize));
tlblCentersDimension :=
round((TResultPointHelpers.distance(topLeft, bottomLeft) / moduleSize));
dimension := TMathUtils.Asr
((tltrCentersDimension + tlblCentersDimension), 1) + 7;
case (dimension and 3) of
0: inc(dimension); // below a valid dimension
1: ; // exactly on a valid dimension e.g. 21, 25, 29, ..., 177
2: dec(dimension); // above a valid dimension
3: Exit(False); // exactly between two valid dimensions
end;
Result := True;
end;
class function TDetector.createTransform(const topLeft, topRight, bottomLeft,
AlignmentPattern: IResultPoint; const dimension: Integer)
: TPerspectiveTransform;
var
bottomRightX, bottomRightY, sourceBottomRightX, sourceBottomRightY,
dimMinusThree: Single;
begin
dimMinusThree := (dimension - 3.5);
if (AlignmentPattern <> nil) then
begin
bottomRightX := AlignmentPattern.X;
bottomRightY := AlignmentPattern.Y;
sourceBottomRightX := (dimMinusThree - 3);
sourceBottomRightY := sourceBottomRightX;
end
else
begin
bottomRightX := ((topRight.X - topLeft.X) + bottomLeft.X);
bottomRightY := ((topRight.Y - topLeft.Y) + bottomLeft.Y);
sourceBottomRightX := dimMinusThree;
sourceBottomRightY := sourceBottomRightX;
end;
Result := TPerspectiveTransform.quadrilateralToQuadrilateral(3.5, 3.5,
dimMinusThree, 3.5, sourceBottomRightX, sourceBottomRightY, 3.5,
dimMinusThree, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRightX,
bottomRightY, bottomLeft.X, bottomLeft.Y);
end;
function TDetector.detect: TDetectorResult;
begin
Result := self.detect(nil)
end;
function TDetector.detect(const hints: TDictionary<TDecodeHintType, TObject>)
: TDetectorResult;
var
obj: TObject;
info: TFinderPatternInfo;
finder: TFinderPatternFinder;
begin
Result := nil;
if ((hints = nil) or
(not hints.ContainsKey(TDecodeHintType.NEED_RESULT_POINT_CALLBACK))) then
FResultPointCallback := nil
else
begin
obj := hints[TDecodeHintType.NEED_RESULT_POINT_CALLBACK];
if (obj is TResultPointEventObject) then
FResultPointCallback := TResultPointEventObject(obj).Event;
end;
finder := TFinderPatternFinder.Create(FImage, ResultPointCallback);
try
info := finder.find(hints);
if Assigned(info) then
begin
Result := processFinderPatternInfo(info);
info.Free;
end;
finally
finder.Free;
end;
end;
function TDetector.processFinderPatternInfo(const info: TFinderPatternInfo)
: TDetectorResult;
var
topLeft, topRight, bottomLeft: IFinderPattern;
moduleSize, bottomRightX, bottomRightY, correctionToTopLeft: Single;
dimension, i, modulesBetweenFPCenters, estAlignmentX, estAlignmentY: Integer;
points: TArray<IResultPoint>;
provisionalVersion: TVersion;
transform: TPerspectiveTransform;
bits: TBitMatrix;
AlignmentPattern: IAlignmentPattern;
begin
Result := nil;
topLeft := info.topLeft;
topRight := info.topRight;
bottomLeft := info.bottomLeft;
moduleSize := calculateModuleSize(topLeft, topRight, bottomLeft);
if (moduleSize < 1.0) then
exit;
if (not TDetector.computeDimension(topLeft, topRight, bottomLeft, moduleSize,
dimension)) then
exit;
provisionalVersion := TVersion.getProvisionalVersionForDimension(dimension);
if (provisionalVersion = nil) then
exit;
modulesBetweenFPCenters := (provisionalVersion.DimensionForVersion - 7);
AlignmentPattern := nil;
// Anything above version 1 has an alignment pattern
if (Length(provisionalVersion.AlignmentPatternCenters) > 0) then
begin
// Guess where a "bottom right" finder pattern would have been
bottomRightX := (topRight.X - topLeft.X) + bottomLeft.X;
bottomRightY := (topRight.Y - topLeft.Y) + bottomLeft.Y;
// Estimate that alignment pattern is closer by 3 modules
// from "bottom right" to known top left location
correctionToTopLeft := (1.0 - (3.0 / modulesBetweenFPCenters));
estAlignmentX :=
Floor(topLeft.X + (correctionToTopLeft * (bottomRightX - topLeft.X)));
estAlignmentY :=
Floor(topLeft.Y + (correctionToTopLeft * (bottomRightY - topLeft.Y)));
i := 4;
while ((i <= 16)) do
begin
AlignmentPattern := findAlignmentInRegion(moduleSize, estAlignmentX,
estAlignmentY, i);
if (AlignmentPattern <> nil) then
break;
i := (i shl 1);
end;
// If we didn't find alignment pattern... well try anyway without it
end;
transform := TDetector.createTransform(topLeft, topRight, bottomLeft,
AlignmentPattern, dimension);
try
bits := TDetector.sampleGrid(FImage, transform, dimension);
finally
transform.Free;
end;
if (bits = nil) then
exit;
if (AlignmentPattern = nil) then
points := TArray<IResultPoint>.Create(bottomLeft, topLeft, topRight)
else
points := TArray<IResultPoint>.Create(bottomLeft, topLeft, topRight,
AlignmentPattern);
Result := TDetectorResult.Create(bits, points);
end;
function TDetector.findAlignmentInRegion(const overallEstModuleSize: Single;
const estAlignmentX, estAlignmentY: Integer; const allowanceFactor: Single)
: IAlignmentPattern;
var
allowance, alignmentAreaRightX, alignmentAreaLeftX, alignmentAreaTopY,
alignmentAreaBottomY: Integer;
alignmentFinder: TAlignmentPatternFinder;
candidateResult: IAlignmentPattern;
begin
Result := nil;
allowance := Floor(allowanceFactor * overallEstModuleSize);
alignmentAreaLeftX := System.Math.Max(0, (estAlignmentX - allowance));
alignmentAreaRightX := System.Math.Min((FImage.Width - 1),
(estAlignmentX + allowance));
if ((alignmentAreaRightX - alignmentAreaLeftX) < (overallEstModuleSize * 3))
then
exit;
alignmentAreaTopY := System.Math.Max(0, (estAlignmentY - allowance));
alignmentAreaBottomY := System.Math.Min((FImage.Height - 1),
(estAlignmentY + allowance));
alignmentFinder := TAlignmentPatternFinder.Create(FImage, alignmentAreaLeftX,
alignmentAreaTopY, (alignmentAreaRightX - alignmentAreaLeftX),
(alignmentAreaBottomY - alignmentAreaTopY), overallEstModuleSize,
self.ResultPointCallback);
candidateResult := alignmentFinder.find;
if candidateResult = nil then
Result := nil
else
Result := candidateResult;
FreeAndNil(alignmentFinder);
end;
class function TDetector.sampleGrid(image: TBitMatrix;
transform: TPerspectiveTransform; dimension: Integer): TBitMatrix;
begin
Result := TDefaultGridSampler.sampleGrid(image, dimension, dimension,
transform)
end;
function TDetector.sizeOfBlackWhiteBlackRun(fromX: Integer; fromY: Integer;
toX: Integer; toY: Integer): Single;
var
steep: boolean;
temp, dx, dy, xstep, ystep, X, Y, state, xlimit, realx, realy, error: Integer;
begin
steep := Abs(toY - fromY) > Abs(toX - fromX);
if (steep) then
begin
temp := fromX;
fromX := fromY;
fromY := temp;
temp := toX;
toX := toY;
toY := temp
end;
dx := Abs(toX - fromX);
dy := Abs(toY - fromY);
error := TMathUtils.Asr(-dx, 1);
if (fromX < toX) then
xstep := 1
else
xstep := -1;
if (fromY < toY) then
ystep := 1
else
ystep := -1;
state := 0;
xlimit := (toX + xstep);
X := fromX;
Y := fromY;
while ((X <> xlimit)) do
begin
if steep then
realx := Y
else
realx := X;
if steep then
realy := X
else
realy := Y;
if ((state = 1) = FImage[realx, realy]) then
begin
if (state = 2) then
begin
Result := TMathUtils.distance(X, Y, fromX, fromY);
exit
end;
inc(state)
end;
inc(error, dy);
if (error > 0) then
begin
if (Y = toY) then
break;;
inc(Y, ystep);
dec(error, dx)
end;
inc(X, xstep)
end;
if (state = 2) then
begin
Result := TMathUtils.distance((toX + xstep), toY, fromX, fromY);
exit
end;
Result := Single.NaN;
end;
function TDetector.sizeOfBlackWhiteBlackRunBothWays(fromX: Integer;
fromY: Integer; toX: Integer; toY: Integer): Single;
var
scale: Double;
otherToX, otherToY: Integer;
begin
Result := sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
scale := 1;
otherToX := (fromX - (toX - fromX));
if (otherToX < 0) then
begin
scale := (fromX / (fromX - otherToX));
otherToX := 0
end
else if (otherToX >= FImage.Width) then
begin
scale := (((FImage.Width - 1) - fromX) / (otherToX - fromX));
otherToX := (FImage.Width - 1)
end;
otherToY := Round(fromY - ((toY - fromY) * scale));
scale := 1;
if (otherToY < 0) then
begin
scale := fromY / (fromY - otherToY);
otherToY := 0
end
else if (otherToY >= FImage.Height) then
begin
scale := (((FImage.Height - 1) - fromY) / (otherToY - fromY));
otherToY := (FImage.Height - 1)
end;
otherToX := Round(fromX + (otherToX - fromX) * scale);
Result := Result + self.sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX,
otherToY);
Result := (Result - 1);
end;
end.
@@ -0,0 +1,147 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.FinderPattern;
interface
uses
ZXing.ResultPoint;
type
/// <summary>
/// As we did for ResultPoint, we use an interfaced object to implement automatic deallocation
/// of TAligmnentPattern instances
/// the actual implementation of this interface is in unit ZXing.QrCode.Internal.FinderPatternImplementation
/// </summary>
IFinderPattern = interface(IResultPoint)
['{EF95BB2F-8863-4A83-840E-699779592EC0}']
function GetCount:integer;
procedure SetCount(value:integer);
/// <summary> <p>Determines if this finder pattern "about equals" a finder pattern at the stated
/// position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
/// </summary>
function aboutEquals(const moduleSize, i, j: Single): Boolean;
/// <summary>
/// Combines this object's current estimate of a finder pattern position and module size
/// with a new estimate. It returns a new {@code FinderPattern} containing a weighted average
/// based on count.
/// </summary>
/// <param name="i">The i.</param>
/// <param name="j">The j.</param>
/// <param name="newModuleSize">New size of the module.</param>
/// <returns></returns>
function combineEstimate(const i, j, newModuleSize: Single): IFinderPattern;
/// <summary>
/// Gets the size of the estimated module.
/// </summary>
/// <value>
/// The size of the estimated module.
/// </value>
function estimatedModuleSize : Single;
property count : Integer read GetCount write SetCount;
end;
/// <summary>
/// contains all static methods for using IFinderPattern instances
/// </summary>
TFinderPatternHelpers= class(TResultPointHelpers)
class function CreateFinderPattern(const posX, posY, estimatedModuleSize: Single;
const count: Integer):IFinderPattern; overload;
class function CreateFinderPattern(const posX, posY, estimatedModuleSize: Single):IFinderPattern; overload;
class procedure orderBestPatterns(const patterns: TArray<IFinderPattern>); static;
end;
implementation
uses ZXing.QrCode.Internal.FinderPatternImplementation;
class function TFinderPatternHelpers.CreateFinderPattern(const posX, posY, estimatedModuleSize: Single; const count: Integer):IFinderPattern;
begin
result := ZXing.QrCode.Internal.FinderPatternImplementation.NewFinderPattern(posX, posY, estimatedModuleSize ,count);
end;
class function TFinderPatternHelpers.CreateFinderPattern(const posX, posY, estimatedModuleSize: Single):IFinderPattern;
begin
result := ZXing.QrCode.Internal.FinderPatternImplementation.NewFinderPattern(posX, posY, estimatedModuleSize);
end;
class procedure TFinderPatternHelpers.orderBestPatterns( const patterns: TArray<IFinderPattern>);
var
zeroOneDistance,
oneTwoDistance,
zeroTwoDistance: Single;
pointA,
pointB,
pointC,
temp: IFinderPattern;
begin
// Find distances between pattern centers
zeroOneDistance := distance(patterns[0], patterns[1]);
oneTwoDistance := distance(patterns[1], patterns[2]);
zeroTwoDistance := distance(patterns[0], patterns[2]);
// Assume one closest to other two is B; A and C will just be guesses at first
if ((oneTwoDistance >= zeroOneDistance) and
(oneTwoDistance >= zeroTwoDistance)) then
begin
pointB := patterns[0];
pointA := patterns[1];
pointC := patterns[2];
end
else if ((zeroTwoDistance >= oneTwoDistance) and
(zeroTwoDistance >= zeroOneDistance)) then
begin
pointB := patterns[1];
pointA := patterns[0];
pointC := patterns[2];
end
else
begin
pointB := patterns[2];
pointA := patterns[0];
pointC := patterns[1];
end;
// Use cross product to figure out whether A and C are correct or flipped.
// This asks whether BC x BA has a positive z component, which is the arrangement
// we want for A, B, C. If it's negative, then we've got it flipped around and
// should swap A and C.
if (TResultPointHelpers.crossProductZ(pointA, pointB, pointC) < 0) then
begin
temp := pointA;
pointA := pointC;
pointC := temp;
end;
patterns[0] := pointA;
patterns[1] := pointB;
patterns[2] := pointC;
end;
end.
@@ -0,0 +1,977 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.FinderPatternFinder;
interface
uses
System.SysUtils,
System.Math,
System.Generics.Defaults,
System.Generics.Collections,
ZXing.Common.BitMatrix,
ZXing.ResultPoint,
ZXing.DecodeHinttype,
ZXing.QrCode.Internal.FinderPatternInfo,
ZXing.QrCode.Internal.FinderPattern;
type
/// <summary>
/// <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
/// markers at three corners of a QR Code.</p>
///
/// <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
/// </summary>
TFinderPatternFinder = class
private
const
CENTER_QUORUM: Integer = 2;
INTEGER_MATH_SHIFT: Integer = 8;
var
FImage: TBitMatrix;
FPossibleCenters: TList<IFinderPattern>;
FhasSkipped: Boolean;
FCrossCheckStateCount: TArray<Integer>;
FresultPointCallback: TResultPointCallback;
class function centerFromEnd(stateCount: TArray<Integer>; pEnd: Integer): Single; static;
function crossCheckDiagonal(startI: Integer; centerJ: Integer; maxCount: Integer; originalStateCountTotal: Integer): boolean;
function crossCheckHorizontal(startJ: Integer; centerI: Integer; maxCount: Integer; originalStateCountTotal: Integer): Single;
function crossCheckVertical(startI: Integer; centerJ: Integer; maxCount: Integer; originalStateCountTotal: Integer): Single;
function findRowSkip: Integer;
function haveMultiplyConfirmedCenters: boolean;
function selectBestPatterns: TArray<IFinderPattern>;
function CrossCheckStateCount: TArray<Integer>;
protected
const
/// <summary>
/// 1 pixel/module times 3 modules/center
/// </summary>
MIN_SKIP: Integer = 3;
/// <summary>
/// support up to version 10 for mobile clients
/// </summary>
MAX_MODULES: Integer = 97;
class function foundPatternCross(const stateCount: TArray<Integer>): Boolean; static;
function handlePossibleCenter(stateCount: TArray<Integer>; i: Integer;
j: Integer): boolean;
property image: TBitMatrix read FImage;
public
/// <summary>
/// <p>Creates a finder that will search the image for three finder patterns.</p>
/// </summary>
/// <param name="image">image to search</param>
constructor Create(const image: TBitMatrix); overload;
/// <summary>
/// Initializes a new instance of the <see cref="FinderPatternFinder"/> class.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="resultPointCallback">The result point callback.</param>
constructor Create(const image: TBitMatrix;
resultPointCallback: TResultPointCallback); overload;
destructor Destroy; override;
function find(const hints: TDictionary<TDecodeHintType, TObject>): TFinderPatternInfo; virtual;
end;
TFurthestFromAverageComparator = class sealed(TInterfacedObject,
IComparer<IFinderPattern>)
private
average: Single;
public
constructor Create(f: Single);
function Compare(const Left, Right: IFinderPattern): Integer;
end;
TCenterComparator = class sealed(TInterfacedObject, IComparer<IFinderPattern>)
private
average: Single;
public
constructor Create(f: Single);
function Compare(const Left, Right: IFinderPattern): Integer;
end;
implementation
{ TFinderPatternFinder }
constructor TFinderPatternFinder.Create(const image: TBitMatrix);
begin
Self.Create(image, TResultPointCallback(nil));
end;
constructor TFinderPatternFinder.Create(const image: TBitMatrix;
resultPointCallback: TResultPointCallback);
begin
FImage := image;
FPossibleCenters := TList<IFinderPattern>.Create;
FCrossCheckStateCount := TArray<Integer>.Create(0, 0, 0, 0, 0);
FresultPointCallback := resultPointCallback;
end;
destructor TFinderPatternFinder.Destroy;
begin
FImage := nil;
{for i := Pred(PossibleCenters.Count) downto 0 do
begin
PossibleCenters[i].Free;
PossibleCenters.Delete(i);
end;}
FPossibleCenters.Free;
FCrossCheckStateCount := nil;
inherited;
end;
class function TFinderPatternFinder.centerFromEnd(stateCount: TArray<Integer>;
pEnd: Integer): Single;
var
aResult: Single;
begin
aResult := (pEnd - stateCount[4] - stateCount[3]) - (stateCount[2] / 2);
if (Single.IsNaN(aResult)) then
begin
result := -1;
exit
end;
result := aResult;
end;
function TFinderPatternFinder.crossCheckDiagonal(startI, centerJ, maxCount,
originalStateCountTotal: Integer): boolean;
var
stateCount: TArray<Integer>;
i, maxI, maxJ, stateCountTotal: Integer;
begin
stateCount := CrossCheckStateCount;
try
i := 0;
while ((((startI >= i) and (centerJ >= i)) and self.image[(centerJ - i),
(startI - i)])) do
begin
inc(stateCount[2]);
inc(i)
end;
if ((startI < i) or (centerJ < i)) then
begin
result := false;
exit
end;
while (((((startI >= i) and (centerJ >= i)) and
not self.image[(centerJ - i), (startI - i)]) and
(stateCount[1] <= maxCount))) do
begin
inc(stateCount[1]);
inc(i)
end;
if (((startI < i) or (centerJ < i)) or (stateCount[1] > maxCount)) then
begin
result := false;
exit
end;
while (((((startI >= i) and (centerJ >= i)) and self.image[(centerJ - i),
(startI - i)]) and (stateCount[0] <= maxCount))) do
begin
inc(stateCount[0]);
inc(i)
end;
if (stateCount[0] > maxCount) then
begin
result := false;
exit
end;
maxI := self.image.Height;
maxJ := self.image.Width;
i := 1;
while (((((startI + i) < maxI) and ((centerJ + i) < maxJ)) and
self.image[(centerJ + i), (startI + i)])) do
begin
inc(stateCount[2]);
inc(i)
end;
if (((startI + i) >= maxI) or ((centerJ + i) >= maxJ)) then
begin
result := false;
exit
end;
while ((((((startI + i) < maxI) and ((centerJ + i) < maxJ)) and
not self.image[(centerJ + i), (startI + i)]) and
(stateCount[3] < maxCount))) do
begin
inc(stateCount[3]);
inc(i)
end;
if ((((startI + i) >= maxI) or ((centerJ + i) >= maxJ)) or
(stateCount[3] >= maxCount)) then
begin
result := false;
exit
end;
while ((((((startI + i) < maxI) and ((centerJ + i) < maxJ)) and
self.image[(centerJ + i), (startI + i)]) and
(stateCount[4] < maxCount))) do
begin
inc(stateCount[4]);
inc(i)
end;
if (stateCount[4] >= maxCount) then
begin
result := false;
exit
end;
stateCountTotal :=
((((stateCount[0] + stateCount[1]) + stateCount[2]) + stateCount[3]) +
stateCount[4]);
Result := (Abs(stateCountTotal - originalStateCountTotal) <
(2 * originalStateCountTotal)) and TFinderPatternFinder.foundPatternCross
(stateCount);
finally
stateCount := nil;
end;
end;
function TFinderPatternFinder.crossCheckHorizontal(startJ: Integer;
centerI: Integer; maxCount: Integer;
originalStateCountTotal: Integer): Single;
var
stateCount: TArray<Integer>;
maxJ, j, stateCountTotal: Integer;
begin
maxJ := FImage.Width;
stateCount := CrossCheckStateCount;
try
j := startJ;
while (((j >= 0) and FImage[j, centerI])) do
begin
inc(stateCount[2]);
dec(j)
end;
if (j < 0) then
begin
result := -1;
exit
end;
while ((((j >= 0) and not FImage[j, centerI]) and
(stateCount[1] <= maxCount))) do
begin
inc(stateCount[1]);
dec(j)
end;
if ((j < 0) or (stateCount[1] > maxCount)) then
begin
result := -1;
exit
end;
while ((((j >= 0) and FImage[j, centerI]) and
(stateCount[0] <= maxCount))) do
begin
inc(stateCount[0]);
dec(j)
end;
if (stateCount[0] > maxCount) then
begin
result := -1;
exit
end;
j := (startJ + 1);
while (((j < maxJ) and FImage[j, centerI])) do
begin
inc(stateCount[2]);
inc(j)
end;
if (j = maxJ) then
begin
result := -1;
exit
end;
while ((((j < maxJ) and not FImage[j, centerI]) and
(stateCount[3] < maxCount))) do
begin
inc(stateCount[3]);
inc(j)
end;
if ((j = maxJ) or (stateCount[3] >= maxCount)) then
begin
result := -1;
exit
end;
while (((j < maxJ) and FImage[j, centerI]) and
(stateCount[4] < maxCount)) do
begin
inc(stateCount[4]);
inc(j)
end;
if (stateCount[4] >= maxCount) then
begin
result := -1;
exit
end;
stateCountTotal :=
((((stateCount[0] + stateCount[1]) + stateCount[2]) + stateCount[3]) +
stateCount[4]);
if (5 * Abs(stateCountTotal - originalStateCountTotal) >=
originalStateCountTotal) then
begin
result := -1;
exit
end;
if TFinderPatternFinder.foundPatternCross(stateCount) then
begin
result := TFinderPatternFinder.centerFromEnd(stateCount, j);
exit;
end;
finally
stateCount := nil;
end;
result := -1;
end;
function TFinderPatternFinder.CrossCheckStateCount: TArray<Integer>;
begin
FCrossCheckStateCount[0] := 0;
FCrossCheckStateCount[1] := 0;
FCrossCheckStateCount[2] := 0;
FCrossCheckStateCount[3] := 0;
FCrossCheckStateCount[4] := 0;
result := FCrossCheckStateCount;
end;
function TFinderPatternFinder.crossCheckVertical(startI: Integer;
centerJ: Integer; maxCount: Integer;
originalStateCountTotal: Integer): Single;
var
stateCount: TArray<Integer>;
maxI, i, stateCountTotal: Integer;
begin
maxI := FImage.Height;
stateCount := CrossCheckStateCount;
i := startI;
try
while (((i >= 0) and FImage[centerJ, i])) do
begin
inc(stateCount[2]);
dec(i)
end;
if (i < 0) then
begin
result := -1;
exit
end;
while ((((i >= 0) and not FImage[centerJ, i]) and
(stateCount[1] <= maxCount))) do
begin
inc(stateCount[1]);
dec(i)
end;
if ((i < 0) or (stateCount[1] > maxCount)) then
begin
result := -1;
exit
end;
while ((((i >= 0) and FImage[centerJ, i]) and
(stateCount[0] <= maxCount))) do
begin
inc(stateCount[0]);
dec(i)
end;
if (stateCount[0] > maxCount) then
begin
result := -1;
exit
end;
i := (startI + 1);
while (((i < maxI) and FImage[centerJ, i])) do
begin
inc(stateCount[2]);
inc(i)
end;
if (i = maxI) then
begin
result := -1;
exit
end;
while ((((i < maxI) and not FImage[centerJ, i]) and
(stateCount[3] < maxCount))) do
begin
inc(stateCount[3]);
inc(i)
end;
if ((i = maxI) or (stateCount[3] >= maxCount)) then
begin
result := -1;
exit
end;
while ((((i < maxI) and FImage[centerJ, i]) and
(stateCount[4] < maxCount))) do
begin
inc(stateCount[4]);
inc(i)
end;
if (stateCount[4] >= maxCount) then
begin
result := -1;
exit
end;
stateCountTotal :=
((((stateCount[0] + stateCount[1]) + stateCount[2]) + stateCount[3]) +
stateCount[4]);
if ((5 * Abs(stateCountTotal - originalStateCountTotal)) >=
(2 * originalStateCountTotal)) then
begin
result := -1;
exit
end;
if TFinderPatternFinder.foundPatternCross(stateCount) then
begin
result := TFinderPatternFinder.centerFromEnd(stateCount, i);
exit;
end;
finally
stateCount := nil;
end;
result := -1;
end;
function TFinderPatternFinder.find(
const hints: TDictionary<TDecodeHintType, TObject>): TFinderPatternInfo;
var
tryHarder,
done, confirmed: Boolean;
maxI, maxJ, iSkip,
i, currentState, j, rowSkip: Integer;
stateCount: TArray<Integer>;
patternInfo: TArray<IFinderPattern>;
resultInfo: TArray<IResultPoint>;
begin
Result := nil;
tryHarder := (hints <> nil) and (hints.ContainsKey(ZXing.DecodeHinttype.TRY_HARDER));
maxI := Self.image.Height;
maxJ := Self.image.Width;
// We are looking for black/white/black/white/black modules in
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
// image, and then account for the center being 3 modules in size. This gives the smallest
// number of pixels the center could be, so skip this often. When trying harder, look for all
// QR versions regardless of how dense they are.
iSkip := (3 * maxI) div (4 * MAX_MODULES);
if (iSkip < MIN_SKIP) or (tryHarder)
then
iSkip := MIN_SKIP;
done := false;
stateCount := TArray<Integer>.Create(0, 0, 0, 0, 0);
try
i := (iSkip - 1);
while ((i < maxI) and (not done)) do
begin
// Get a row of black/white values
stateCount[0] := 0;
stateCount[1] := 0;
stateCount[2] := 0;
stateCount[3] := 0;
stateCount[4] := 0;
currentState := 0;
j := 0;
while (j < maxJ) do
begin
if (FImage[j, i]) then
begin
// Black pixel
if ((currentState and 1) = 1)
then
Inc(currentState);
Inc(stateCount[currentState]);
end else
begin
// White pixel
if ((currentState and 1) = 0) then
begin
// Counting black pixels
if (currentState = 4) then
begin
// A winner?
if TFinderPatternFinder.foundPatternCross(stateCount) then
begin
// Yes
confirmed := handlePossibleCenter(stateCount, i, j);
if (confirmed) then
begin
// Start examining every other line. Checking each line turned out to be too
// expensive and didn't improve performance.
iSkip := 2;
if (FhasSkipped)
then
done := haveMultiplyConfirmedCenters()
else
begin
rowSkip := findRowSkip();
if (rowSkip > stateCount[2]) then
begin
// Skip rows between row of lower confirmed center
// and top of presumed third confirmed center
// but back up a bit to get a full chance of detecting
// it, entire width of center of finder pattern
// Skip by rowSkip, but back off by stateCount[2] (size of last center
// of pattern we saw) to be conservative, and also back off by iSkip which
// is about to be re-added
Inc(i, ((rowSkip - stateCount[2]) - iSkip));
j := (maxJ - 1);
end;
end;
end else
begin
stateCount[0] := stateCount[2];
stateCount[1] := stateCount[3];
stateCount[2] := stateCount[4];
stateCount[3] := 1;
stateCount[4] := 0;
currentState := 3;
continue;
end;
// Clear state to start looking again
currentState := 0;
stateCount[0] := 0;
stateCount[1] := 0;
stateCount[2] := 0;
stateCount[3] := 0;
stateCount[4] := 0
end else
begin
// No, shift counts back by two
stateCount[0] := stateCount[2];
stateCount[1] := stateCount[3];
stateCount[2] := stateCount[4];
stateCount[3] := 1;
stateCount[4] := 0;
currentState := 3;
end;
end else
begin
Inc(currentState);
Inc(stateCount[currentState]);
end;
end else
// Counting white pixels
Inc(stateCount[currentState]);
end;
Inc(j);
end;
if foundPatternCross(stateCount) then
begin
confirmed := Self.handlePossibleCenter(stateCount, i, maxJ);
if (confirmed) then
begin
iSkip := stateCount[0];
if (FhasSkipped)
then
// Found a third one
done := self.haveMultiplyConfirmedCenters
end;
end;
Inc(i, iSkip);
end;
patternInfo := selectBestPatterns();
if (patternInfo = nil)
then
exit;
TFinderPatternHelpers.orderBestPatterns(patternInfo);
Result := TFinderPatternInfo.Create(patternInfo);
finally
resultInfo := nil;
stateCount := nil;
patternInfo := nil;
end;
end;
function TFinderPatternFinder.findRowSkip: Integer;
var
center: IFinderPattern;
firstConfirmedCenter: IResultPoint;
begin
Result := 0;
if (FPossibleCenters.Count > 1) then
begin
firstConfirmedCenter := nil;
for center in FPossibleCenters do
begin
if (center.Count >= 2) then
begin
if (firstConfirmedCenter <> nil) then
begin
FhasSkipped := true;
Result := Floor(Abs(firstConfirmedCenter.X - center.X) -
Abs(firstConfirmedCenter.Y - center.Y)) div 2;
exit;
end;
firstConfirmedCenter := center;
end
end
end;
end;
/// <param name="stateCount">count of black/white/black/white/black pixels just read
/// </param>
/// <returns> true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios
/// used by finder patterns to be considered a match
/// </returns>
class function TFinderPatternFinder.foundPatternCross(
const stateCount: TArray<Integer>): Boolean;
var
i, totalModuleSize,
count, moduleSize,
maxVariance: Integer;
begin
Result := false;
totalModuleSize := 0;
for i := 0 to Pred(5) do
begin
count := stateCount[i];
if (count = 0)
then
exit;
Inc(totalModuleSize, count);
end;
if (totalModuleSize < 7)
then
exit;
moduleSize := ((totalModuleSize shl 8) div 7);
maxVariance := (moduleSize div 2);
// Allow less than 50% variance from 1-1-3-1-1 proportions
Result := (Abs(moduleSize - (stateCount[0] shl INTEGER_MATH_SHIFT)) < maxVariance) and
(Abs(moduleSize - (stateCount[1] shl INTEGER_MATH_SHIFT)) < maxVariance) and
(Abs(3 * moduleSize - (stateCount[2] shl INTEGER_MATH_SHIFT)) < (3 * maxVariance)) and
(Abs(moduleSize - (stateCount[3] shl INTEGER_MATH_SHIFT)) < maxVariance) and
(Abs(moduleSize - (stateCount[4] shl INTEGER_MATH_SHIFT)) < maxVariance);
end;
function TFinderPatternFinder.handlePossibleCenter(stateCount: TArray<Integer>;
i, j: Integer): boolean;
var
stateCountTotal, index: Integer;
centerJ, centerI: Single;
center, point: IFinderPattern;
found: boolean;
estimatedModuleSize: Single;
begin
Result := false;
center := nil;
stateCountTotal := ((((stateCount[0] + stateCount[1]) + stateCount[2]) +
stateCount[3]) + stateCount[4]);
centerJ := TFinderPatternFinder.centerFromEnd(stateCount, j);
if (centerJ = -1)
then
exit;
centerI := self.crossCheckVertical(i, Floor(centerJ), stateCount[2],
stateCountTotal);
if (centerI = -1)
then
exit;
centerJ := crossCheckHorizontal(Floor(centerJ), Floor(centerI), stateCount[2],
stateCountTotal);
if ((centerJ <> -1) and (self.crossCheckDiagonal(Floor(centerI), Floor(centerJ), stateCount[2],
stateCountTotal))) then
begin
estimatedModuleSize := stateCountTotal / 7;
found := false;
index := 0;
while ((index < FPossibleCenters.Count)) do
begin
center := FPossibleCenters[index];
if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) then
begin
FPossibleCenters[index] := nil;
FPossibleCenters.Delete(index);
FPossibleCenters.Insert(index, center.combineEstimate(centerI, centerJ,
estimatedModuleSize));
found := true;
break;
end;
inc(index)
end;
if (not found) then
begin
point := TFinderPatternHelpers.CreateFinderPattern(centerJ, centerI, estimatedModuleSize);
FPossibleCenters.Add(point);
if Assigned(FresultPointCallback)
then
FresultPointCallBack(point);
end;
Result := true;
end;
end;
function TFinderPatternFinder.haveMultiplyConfirmedCenters: boolean;
var
confirmedCount, max, i: Integer;
totalModuleSize, average, totalDeviation: Single;
pattern: IFinderPattern;
begin
confirmedCount := 0;
totalModuleSize := 0;
max := FPossibleCenters.Count;
for pattern in FPossibleCenters do
begin
if (pattern.Count >= CENTER_QUORUM) then
begin
inc(confirmedCount);
totalModuleSize := totalModuleSize + pattern.estimatedModuleSize;
end;
end;
if (confirmedCount < 3) then
begin
result := false;
exit
end;
average := totalModuleSize / max;
totalDeviation := 0;
i := 0;
while ((i < max)) do
begin
pattern := FPossibleCenters[i];
totalDeviation := totalDeviation +
Abs(pattern.estimatedModuleSize - average);
inc(i)
end;
result := (totalDeviation <= (0.05 * totalModuleSize));
end;
function TFinderPatternFinder.selectBestPatterns: TArray<IFinderPattern>;
var
average, square, totalModuleSize: Double;
size, stdDev, limit, avrsng: Single;
center, possibleCenter, pattern: IFinderPattern;
startSize, i: Integer;
comparator: IComparer<IFinderPattern>;
begin
startSize := FPossibleCenters.Count;
if (startSize < 3) then
begin
result := nil;
exit
end;
if (startSize > 3) then
begin
totalModuleSize := 0;
square := 0;
for center in FPossibleCenters do
begin
size := center.estimatedModuleSize;
totalModuleSize := totalModuleSize + size;
square := square + (size * size);
end;
average := totalModuleSize / startSize;
stdDev := Sqrt((square / startSize) - (average * average));
// this intermediate assignment is a workaround for old-gen compilers
// that are not deallocating TFurthestFromAverageComparator instance
// (bug verified on XE8: just declare a destructor Destroy override
// in TFurthestFromAverageComparator put a breakpoint in it and see
// that the destructor never gets called if you don't use this intermediate
// assignment)
avrsng := average;
comparator := TFurthestFromAverageComparator.Create(avrsng);
FPossibleCenters.Sort(comparator);
limit := System.Math.Max((0.2 * avrsng), stdDev);
i := 0;
while (((i < FPossibleCenters.Count) and
(FPossibleCenters.Count > 3))) do
begin
pattern := FPossibleCenters[i];
if (Abs(pattern.estimatedModuleSize - average) > limit) then
begin
FPossibleCenters.Delete(i);
dec(i)
end;
inc(i)
end
end;
if (FPossibleCenters.Count > 3) then
begin
totalModuleSize := 0;
for possibleCenter in FPossibleCenters do
begin
totalModuleSize := totalModuleSize + possibleCenter.estimatedModuleSize;
end;
average := totalModuleSize / FPossibleCenters.Count;
// this intermediate assignment is a workaround for old-gen compilers
// that are not deallocating TCenterComparator instance
// (bug verified on XE8: just declare a destructor Destroy override in
// TCenterComparator,put a breakpoint in it and see that the destructor
// never gets called if you don't use this intermediate assignment)
comparator := TCenterComparator.Create(average);
FPossibleCenters.Sort( comparator );
// if (PossibleCenters.Count > 4) then
// begin
// self.PossibleCenters.DeleteRange(4, self.PossibleCenters.Count - 1);
// end;
end;
result := TArray<IFinderPattern>.Create(FPossibleCenters[0],
FPossibleCenters[1], FPossibleCenters[2]);
end;
{ TFurthestFromAverageComparator }
function TFurthestFromAverageComparator.Compare(const Left,
Right: IFinderPattern): Integer;
var
dA, dB: Single;
begin
dA := Abs(Right.estimatedModuleSize - self.average);
dB := Abs(Left.estimatedModuleSize - self.average);
if (dA < dB) then
begin
result := -1;
end
else if (dA = dB) then
begin
result := 0;
end
else
result := 1;
end;
constructor TFurthestFromAverageComparator.Create(f: Single);
begin
average := f;
end;
{ TCenterComparator }
function TCenterComparator.Compare(const Left, Right: IFinderPattern): Integer;
var
dA, dB: Single;
begin
if (Right.Count = Left.Count) then
begin
dA := Abs(Right.estimatedModuleSize - self.average);
dB := Abs(Left.estimatedModuleSize - self.average);
if (dA < dB) then
begin
result := 1;
end
else if (dA = dB) then
begin
result := 0;
end
else
result := -1;
exit;
end;
result := (Right.Count - Left.Count);
end;
constructor TCenterComparator.Create(f: Single);
begin
average := f;
end;
end.
@@ -0,0 +1,152 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.FinderPatternImplementation;
interface
uses
ZXing.QrCode.Internal.FinderPattern;
function NewFinderPattern(const posX, posY, estimatedModuleSize: Single; const count: Integer):IFinderPattern; overload;
function NewFinderPattern(const posX, posY, estimatedModuleSize: Single):IFinderPattern; overload;
implementation
uses ZXing.ResultPoint,ZXing.ResultPointImplementation;
{ TFinderPattern }
type
/// <summary>
/// <p>Encapsulates a finder pattern, which are the three square patterns found in
/// the corners of QR Codes. It also encapsulates a count of similar finder patterns,
/// as a convenience to the finder's bookkeeping.</p>
/// </summary>
TFinderPattern = class(TResultPoint,IResultPoint,IFinderPattern)
private
Fcount: Integer;
FestimatedModuleSize: Single;
function GetCount:integer;
procedure SetCount(value:integer);
constructor Create(const posX, posY, estimatedModuleSize: Single;
const count: Integer); overload;
constructor Create(const posX, posY, estimatedModuleSize: Single); overload;
/// <summary> <p>Determines if this finder pattern "about equals" a finder pattern at the stated
/// position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
/// </summary>
function aboutEquals(const moduleSize, i, j: Single): Boolean;
/// <summary>
/// Combines this object's current estimate of a finder pattern position and module size
/// with a new estimate. It returns a new {@code FinderPattern} containing a weighted average
/// based on count.
/// </summary>
/// <param name="i">The i.</param>
/// <param name="j">The j.</param>
/// <param name="newModuleSize">New size of the module.</param>
/// <returns></returns>
function combineEstimate(const i, j, newModuleSize: Single): IFinderPattern;
/// <summary>
/// Gets the size of the estimated module.
/// </summary>
/// <value>
/// The size of the estimated module.
/// </value>
function estimatedModuleSize : Single;
property count : Integer read GetCount write SetCount;
end;
constructor TFinderPattern.Create(const posX, posY,
estimatedModuleSize: Single; const count: Integer);
begin
inherited Create(posX, posY);
FestimatedModuleSize := estimatedModuleSize;
Fcount := count;
end;
constructor TFinderPattern.Create(const posX, posY,
estimatedModuleSize: Single);
begin
Self.Create(posX, posY, estimatedModuleSize, 1);
end;
function TFinderPattern.estimatedModuleSize: Single;
begin
result := FestimatedModuleSize;
end;
function TFinderPattern.GetCount: integer;
begin
result := FCount;
end;
function TFinderPattern.aboutEquals(const moduleSize, i, j: Single): Boolean;
var
moduleSizeDiff : Single;
begin
Result := false;
if ((Abs(i - y) <= moduleSize) and (Abs(j - x) <= moduleSize)) then
begin
moduleSizeDiff := Abs(moduleSize - self.estimatedModuleSize);
Result := ((moduleSizeDiff <= 1) or
(moduleSizeDiff <= self.estimatedModuleSize));
end;
end;
procedure TFinderPattern.SetCount(value: integer);
begin
FCount := value;
end;
function TFinderPattern.combineEstimate(const i, j,
newModuleSize: Single): IFinderPattern;
var
combinedCount: Integer;
combinedX, combinedY: Single;
begin
combinedCount := (Self.count + 1);
combinedX := ((Self.count * x) + j) / combinedCount;
combinedY := ((Self.count * y) + i) / combinedCount;
Result := TFinderPattern.Create(combinedX, combinedY,
(((Self.count * Self.estimatedModuleSize) + newModuleSize) / combinedCount),
combinedCount)
end;
function NewFinderPattern(const posX, posY, estimatedModuleSize: Single; const count: Integer):IFinderPattern;
begin
result := TFinderPattern.Create( posX, posY, estimatedModuleSize,count);
end;
function NewFinderPattern(const posX, posY, estimatedModuleSize: Single):IFinderPattern;
begin
result := TFinderPattern.Create( posX, posY, estimatedModuleSize);
end;
end.
@@ -0,0 +1,69 @@
{
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.FinderPatternInfo;
interface
uses
ZXing.QrCode.Internal.FinderPattern;
type
/// <summary>
/// <p>Encapsulates information about finder patterns in an image, including the location of
/// the three finder patterns, and their estimated module size.</p>
/// </summary>
TFinderPatternInfo = class sealed
private
FbottomLeft: IFinderPattern;
FtopLeft: IFinderPattern;
FtopRight: IFinderPattern;
public
/// <summary>
/// Initializes a new instance of the <see cref="FinderPatternInfo"/> class.
/// </summary>
/// <param name="patternCenters">The pattern centers.</param>
constructor Create(const patternCenters: TArray<IFinderPattern>);
/// <summary>
/// Gets the bottom left.
/// </summary>
property bottomLeft : IFinderPattern read FbottomLeft;
/// <summary>
/// Gets the top left.
/// </summary>
property topLeft : IFinderPattern read FtopLeft;
/// <summary>
/// Gets the top right.
/// </summary>
property topRight : IFinderPattern read FtopRight;
end;
implementation
constructor TFinderPatternInfo.Create(const patternCenters: TArray<IFinderPattern>);
begin
FbottomLeft := patternCenters[0];
FtopLeft := patternCenters[1];
FtopRight := patternCenters[2]
end;
end.
@@ -0,0 +1,258 @@
{
* 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.
* Original Author: bbrown@google.com (Brian Brown)
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.DataMatrixReader;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.Common.Detector.MathUtils,
Math,
ZXing.Common.BitArray,
ZXing.BarcodeFormat,
ZXing.ReadResult,
ZXing.Reader,
ZXing.DecodeHintType,
ZXing.DecoderResult,
ZXing.Common.DetectorResult,
ZXing.ResultMetadataType,
ZXing.ResultPoint,
ZXing.Common.BitMatrix,
ZXing.BinaryBitmap,
ZXing.Datamatrix.Internal.Decoder,
ZXing.Datamatrix.Internal.Detector;
type
/// <summary>
/// This implementation can detect and decode Data Matrix codes in an image.
/// </summary>
TDataMatrixReader = class(TInterfacedObject, IReader)
private
FDecoder: TDataMatrixDecoder;
NO_POINTS: TArray<IResultPoint>;
/// <summary>
/// This method detects a code in a "pure" image -- that is, pure monochrome image
/// which contains only an unrotated, unskewed, image of a code, with some white border
/// around it. This is a specialized method that works exceptionally fast in this special
/// case.
///
/// <seealso cref="ZXing.QrCode.QRCodeReader.extractPureBits(TBitMatrix)" />
/// </summary>
class function extractPureBits(const image: TBitMatrix): TBitMatrix; static;
class function moduleSize(const leftTopBlack: TArray<Integer>;
const image: TBitMatrix; var moduleSize: Integer): Boolean; static;
public
constructor Create;
destructor Destroy; override;
/// <summary>
/// This implementation can detect and decode Data Matrix codes in an image.
/// </summary>
function decode(const image: TBinaryBitmap): TReadResult; overload;
function decode(const image: TBinaryBitmap;
hints: TDictionary<TDecodeHintType, TObject>): TReadResult; overload;
procedure reset;
end;
implementation
uses ZXing.ByteSegments;
{ TDataMatrixReader }
constructor TDataMatrixReader.Create;
begin
inherited;
FDecoder := TDataMatrixDecoder.Create;
NO_POINTS := TArray<IResultPoint>.Create();
end;
destructor TDataMatrixReader.Destroy;
begin
NO_POINTS := nil;
FDecoder.Free;
inherited;
end;
function TDataMatrixReader.decode(const image: TBinaryBitmap): TReadResult;
begin
Result := decode(image, nil);
end;
function TDataMatrixReader.decode(const image: TBinaryBitmap;
hints: TDictionary<TDecodeHintType, TObject>): TReadResult;
var
matrixDetector: TDataMatrixDetector;
DecoderResult: TDecoderResult;
points: TArray<IResultPoint>;
bits: TBitMatrix;
DetectorResult: TDetectorResult;
ByteSegments: IByteSegments;
begin
Result := nil;
DetectorResult := nil;
DecoderResult := nil;
try
if ((hints <> nil) and hints.ContainsKey(TDecodeHintType.PURE_BARCODE)) then
begin
bits := TDataMatrixReader.extractPureBits(image.BlackMatrix);
if Assigned(bits) then
begin
DecoderResult := FDecoder.decode(bits);
points := NO_POINTS;
FreeAndNil(bits);
end
else
exit;
end
else
begin
matrixDetector := TDataMatrixDetector.Create(image.BlackMatrix);
try
DetectorResult := matrixDetector.detect();
finally
matrixDetector.Free;
end;
if (DetectorResult = nil) then
exit;
DecoderResult := FDecoder.decode(DetectorResult.bits);
points := DetectorResult.points;
end;
if (DecoderResult = nil) then
exit;
Result := TReadResult.Create(DecoderResult.Text, DecoderResult.RawBytes,
points, TBarcodeFormat.DATA_MATRIX);
ByteSegments := DecoderResult.ByteSegments;
if (ByteSegments <> nil) then
Result.putMetadata(TResultMetadataType.BYTE_SEGMENTS, TResultMetaData.CreateByteSegmentsMetadata( byteSegments));
if (Length(DecoderResult.ECLevel) <> 0) then
Result.putMetadata(TResultMetadataType.ERROR_CORRECTION_LEVEL, TResultMetaData.CreateStringMetadata(DecoderResult.ECLevel));
finally
byteSegments:=nil;
if Assigned(DetectorResult) then
FreeAndNil(DetectorResult);
if Assigned(DecoderResult) then
FreeAndNil(DecoderResult);
end;
end;
class function TDataMatrixReader.extractPureBits(const image: TBitMatrix)
: TBitMatrix;
var
moduleSize: Integer;
leftTopBlack, rightBottomBlack: TArray<Integer>;
top, bottom, left, right: Integer;
matrixWidth, matrixHeight: Integer;
nudge: Integer;
bits: TBitMatrix;
x, y, iOffset: Integer;
begin
Result := nil;
leftTopBlack := image.getTopLeftOnBit;
rightBottomBlack := image.getBottomRightOnBit;
if ((leftTopBlack = nil) or (rightBottomBlack = nil)) then
exit;
if (not TDataMatrixReader.moduleSize(leftTopBlack, image, moduleSize)) then
exit;
top := leftTopBlack[1];
bottom := rightBottomBlack[1];
left := leftTopBlack[0];
right := rightBottomBlack[0];
matrixWidth := ((right - left + 1) div moduleSize);
matrixHeight := ((bottom - top + 1) div moduleSize);
if ((matrixWidth <= 0) or (matrixHeight <= 0)) then
exit;
// 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.
nudge := TMathUtils.Asr(moduleSize,1);
Inc(top, nudge);
Inc(left, nudge);
// Now just read off the bits
bits := TBitMatrix.Create(matrixWidth, matrixHeight);
for y := 0 to Pred(matrixHeight) do
begin
iOffset := (top + (y * moduleSize));
for x := 0 to Pred(matrixWidth) do
begin
if (image[(left + (x * moduleSize)), iOffset]) then
bits[x, y] := true;
end;
end;
Result := bits;
end;
class function TDataMatrixReader.moduleSize(const leftTopBlack: TArray<Integer>;
const image: TBitMatrix; var moduleSize: Integer): Boolean;
var
width, x, y: Integer;
begin
Result := false;
width := image.width;
x := leftTopBlack[0];
y := leftTopBlack[1];
while ((x < width) and image[x, y]) do
Inc(x);
if (x = width) then
begin
moduleSize := 0;
exit;
end;
moduleSize := (x - leftTopBlack[0]);
if (moduleSize = 0) then
exit;
Result := true;
end;
procedure TDataMatrixReader.reset;
begin
// do nothing
end;
end.
@@ -0,0 +1,340 @@
{
* 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.QRCodeReader;
interface
uses
System.SysUtils,
System.Math,
System.Generics.Collections,
ZXing.Common.BitArray,
ZXing.ReadResult,
ZXing.Reader,
ZXing.DecodeHintType,
ZXing.BarcodeFormat,
ZXing.ResultPoint,
ZXing.BinaryBitmap,
ZXing.QrCode.Internal.Decoder,
ZXing.Common.BitMatrix,
ZXing.DecoderResult,
ZXing.ResultMetadataType,
ZXing.Common.DetectorResult,
ZXing.QrCode.Internal.QRCodeDecoderMetaData,
ZXing.QrCode.Internal.Detector,
ZXing.QrCode.Internal.DecodedBitStreamParser;
type
/// <summary>
/// This implementation can detect and decode QR Codes in an image.
/// </summary>
TQRCodeReader = class(TInterfacedObject, IReader)
private
FDecoder: TQRDecoder;
NO_POINTS: TArray<IResultPoint>;
/// <summary>
/// This method detects a code in a "pure" image -- that is, pure monochrome image
/// which contains only an unrotated, unskewed, image of a code, with some white border
/// around it. This is a specialized method that works exceptionally fast in this special
/// case.
///
/// <seealso cref="ZXing.Datamatrix.DataMatrixReader.extractPureBits(TBitMatrix)" />
/// </summary>
class function extractPureBits(const image: TBitMatrix): TBitMatrix; static;
class function moduleSize(const leftTopBlack: TArray<Integer>;
const image: TBitMatrix; var msize: Single): Boolean; static;
protected
/// <summary>
/// Gets the decoder.
/// </summary>
/// <returns></returns>
property Decoder: TQRDecoder read FDecoder;
public
constructor Create;
destructor Destroy; override;
/// <summary>
/// Locates and decodes a QR code in an image.
///
/// <returns>a String representing the content encoded by the QR code</returns>
/// </summary>
function decode(const image: TBinaryBitmap): TReadResult; overload;
/// <summary>
/// Locates and decodes a barcode in some format within an image. This method also accepts
/// hints, each possibly associated to some data, which may help the implementation decode.
/// </summary>
/// <param name="image">image of barcode to decode</param>
/// <param name="hints">passed as a <see cref="TDictionary{TKey, TValue}"/> from <see cref="TDecodeHintType"/>
/// to arbitrary data. The
/// meaning of the data depends upon the hint type. The implementation may or may not do
/// anything with these hints.</param>
/// <returns>
/// String which the barcode encodes
/// </returns>
function decode(const image: TBinaryBitmap;
hints: TDictionary<TDecodeHintType, TObject>): TReadResult; overload;
/// <summary>
/// Resets any internal state the implementation has after a decode, to prepare it
/// for reuse.
/// </summary>
procedure reset;
end;
implementation
uses ZXing.ByteSegments;
{ TQRCodeReader }
constructor TQRCodeReader.Create;
begin
inherited;
FDecoder := TQRDecoder.Create;
NO_POINTS := TArray<IResultPoint>.Create();
end;
destructor TQRCodeReader.Destroy;
begin
NO_POINTS := nil;
FDecoder.Free;
inherited;
end;
function TQRCodeReader.decode(const image: TBinaryBitmap): TReadResult;
begin
Result := Self.decode(image, nil);
end;
function TQRCodeReader.decode(const image: TBinaryBitmap;
hints: TDictionary<TDecodeHintType, TObject>): TReadResult;
var
DecoderResult: TDecoderResult;
Detector: TDetector;
points: TArray<IResultPoint>;
bits: TBitMatrix;
DetectorResult: TDetectorResult;
data: TQRCodeDecoderMetaData;
byteSegments: IByteSegments;
begin
Result := nil;
DecoderResult := nil;
if ((image = nil) or (image.BlackMatrix = nil)) then
// something is wrong with the image
exit;
try
if ((hints <> nil) and hints.ContainsKey(TDecodeHintType.PURE_BARCODE)) then
begin
bits := TQRCodeReader.extractPureBits(image.BlackMatrix);
if Assigned(bits) then
begin
DecoderResult := Decoder.decode(bits, hints);
points := NO_POINTS;
bits.Free;
end
else
exit;
end
else
begin
Detector := TDetector.Create(image.BlackMatrix);
try
DetectorResult := Detector.detect(hints);
if Assigned(DetectorResult) then
begin
DecoderResult := Decoder.decode(DetectorResult.bits, hints);
points := DetectorResult.points;
DetectorResult.Free;
end
else
exit;
finally
Detector.Free;
end;
end;
if (DecoderResult = nil) then
exit;
// If the code was mirrored: swap the bottom-left and the top-right points.
data := TQRCodeDecoderMetaData(DecoderResult.Other);
if (data <> nil) then
data.applyMirroredCorrection(points);
Result := TReadResult.Create(DecoderResult.Text, DecoderResult.RawBytes,
points, TBarcodeFormat.QR_CODE);
byteSegments := DecoderResult.byteSegments;
if (byteSegments <> nil) then
Result.putMetadata(TResultMetadataType.BYTE_SEGMENTS, TResultMetaData.CreateByteSegmentsMetadata(byteSegments));
if (Length(DecoderResult.ecLevel) <> 0) then
Result.putMetadata(TResultMetadataType.ERROR_CORRECTION_LEVEL, TResultMetaData.CreateStringMetadata(DecoderResult.ecLevel));
if (DecoderResult.StructuredAppend) then
begin
Result.putMetadata(TResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
TResultMetaData.CreateIntegerMetadata(DecoderResult.StructuredAppendSequenceNumber));
Result.putMetadata(TResultMetadataType.STRUCTURED_APPEND_PARITY,
TResultMetaData.CreateIntegerMetadata(DecoderResult.StructuredAppendParity))
end;
finally
if Assigned(DecoderResult) then
FreeAndNil(DecoderResult);
end;
end;
procedure TQRCodeReader.reset;
begin
// do nothing
end;
class function TQRCodeReader.extractPureBits(const image: TBitMatrix)
: TBitMatrix;
var
moduleSize: Single;
leftTopBlack, rightBottomBlack: TArray<Integer>;
top, bottom, left, right, matrixWidth, matrixHeight, nudge, x, y, iOffset,
nudgedTooFarRight, nudgedTooFarDown: Integer;
bits: TBitMatrix;
begin
Result := nil;
leftTopBlack := image.getTopLeftOnBit;
rightBottomBlack := image.getBottomRightOnBit;
if ((leftTopBlack = nil) or (rightBottomBlack = nil)) then
exit;
if (not TQRCodeReader.moduleSize(leftTopBlack, image, moduleSize)) then
exit;
top := leftTopBlack[1];
bottom := rightBottomBlack[1];
left := leftTopBlack[0];
right := rightBottomBlack[0];
// Sanity check!
if ((left >= right) or (top >= bottom)) then
exit;
if ((bottom - top) <> (right - left)) then
// Special case, where bottom-right module wasn't black so we found something else in the last row
// Assume it's a square, so use height as the width
right := (left + (bottom - top));
matrixWidth := Round(((right - left) + 1) / moduleSize);
matrixHeight := Round(((bottom - top) + 1) / moduleSize);
if ((matrixWidth <= 0) or (matrixHeight <= 0)) then
exit;
if (matrixHeight <> matrixWidth) then
// Only possibly decode square regions
exit;
// 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.
nudge := Trunc(moduleSize / 2.0);
Inc(top, nudge);
Inc(left, nudge);
// But careful that this does not sample off the edge
// "right" is the farthest-right valid pixel location -- right+1 is not necessarily
// This is positive by how much the inner x loop below would be too large
nudgedTooFarRight := left + Trunc((matrixWidth - 1) * moduleSize) - right;
if (nudgedTooFarRight > 0) then
begin
if (nudgedTooFarRight > nudge) then
// Neither way fits; abort
exit;
Dec(left, nudgedTooFarRight);
end;
// See logic above
nudgedTooFarDown := top + Trunc((matrixHeight - 1) * moduleSize) - bottom;
if (nudgedTooFarDown > 0) then
begin
if (nudgedTooFarDown > nudge) then
// Neither way fits; abort
exit;
Dec(top, nudgedTooFarDown);
end;
// Now just read off the bits
bits := TBitMatrix.Create(matrixWidth, matrixHeight);
for y := 0 to Pred(matrixHeight) do
begin
iOffset := top + Trunc(y * moduleSize);
for x := 0 to Pred(matrixWidth) do
begin
if (image[left + Trunc((x * moduleSize)), iOffset]) then
bits[x, y] := true;
end;
end;
Result := bits;
end;
class function TQRCodeReader.moduleSize(const leftTopBlack: TArray<Integer>;
const image: TBitMatrix; var msize: Single): Boolean;
var
height, width, x, y: Integer;
inBlack: Boolean;
transitions: Integer;
begin
Result := false;
height := image.height;
width := image.width;
x := leftTopBlack[0];
y := leftTopBlack[1];
inBlack := true;
transitions := 0;
while (((x < width) and (y < height))) do
begin
if (inBlack <> image[x, y]) then
begin
Inc(transitions);
if (transitions = 5) then
break;
inBlack := not inBlack;
end;
Inc(x);
Inc(y);
end;
if ((x = width) or (y = height)) then
begin
msize := 0.0;
exit;
end;
msize := ((x - leftTopBlack[0]) / 7.0);
Result := true;
end;
end.