{
* 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
///
Encapsulates a QR Code's format information, including the data mask used and
/// error correction level.
///
///
///
///
///
///
TFormatInformation = class sealed
private
FErrorCorrectionLevel: TErrorCorrectionLevel;
FDataMask: Byte;
const
FORMAT_INFO_MASK_QR: Integer = $5412;
/// See ISO 18004:2006, Annex C, Table C.1
class var FORMAT_INFO_DECODE_LOOKUP: TArray>;
/// Offset i holds the number of 1 bits in the binary representation of i
class var BITS_SET_IN_HALF_BYTE: TArray;
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;
///
/// Decodes the format information.
///
/// format info indicator, with mask still applied
/// The masked format info2.
///
/// information about the format it specifies, or null
/// if doesn't seem to match any known pattern
///
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;
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.Create($5412, 0);
FORMAT_INFO_DECODE_LOOKUP[1] := TArray.Create($5125, 1);
FORMAT_INFO_DECODE_LOOKUP[2] := TArray.Create($5E7C, 2);
FORMAT_INFO_DECODE_LOOKUP[3] := TArray.Create($5B4B, 3);
FORMAT_INFO_DECODE_LOOKUP[4] := TArray.Create($45F9, 4);
FORMAT_INFO_DECODE_LOOKUP[5] := TArray.Create($40CE, 5);
FORMAT_INFO_DECODE_LOOKUP[6] := TArray.Create($4F97, 6);
FORMAT_INFO_DECODE_LOOKUP[7] := TArray.Create($4AA0, 7);
FORMAT_INFO_DECODE_LOOKUP[8] := TArray.Create($77C4, 8);
FORMAT_INFO_DECODE_LOOKUP[9] := TArray.Create($72F3, 9);
FORMAT_INFO_DECODE_LOOKUP[10] := TArray.Create($7DAA, 10);
FORMAT_INFO_DECODE_LOOKUP[11] := TArray.Create($789D, 11);
FORMAT_INFO_DECODE_LOOKUP[12] := TArray.Create($662F, 12);
FORMAT_INFO_DECODE_LOOKUP[13] := TArray.Create($6318, 13);
FORMAT_INFO_DECODE_LOOKUP[14] := TArray.Create($6C41, 14);
FORMAT_INFO_DECODE_LOOKUP[15] := TArray.Create($6976, 15);
FORMAT_INFO_DECODE_LOOKUP[$10] := TArray.Create($1689, $10);
FORMAT_INFO_DECODE_LOOKUP[$11] := TArray.Create($13BE, $11);
FORMAT_INFO_DECODE_LOOKUP[$12] := TArray.Create($1CE7, $12);
FORMAT_INFO_DECODE_LOOKUP[$13] := TArray.Create($19D0, $13);
FORMAT_INFO_DECODE_LOOKUP[20] := TArray.Create($762, 20);
FORMAT_INFO_DECODE_LOOKUP[$15] := TArray.Create($255, $15);
FORMAT_INFO_DECODE_LOOKUP[$16] := TArray.Create($D0C, $16);
FORMAT_INFO_DECODE_LOOKUP[$17] := TArray.Create($83B, $17);
FORMAT_INFO_DECODE_LOOKUP[$18] := TArray.Create($355F, $18);
FORMAT_INFO_DECODE_LOOKUP[$19] := TArray.Create($3068, $19);
FORMAT_INFO_DECODE_LOOKUP[$1A] := TArray.Create($3F31, $1A);
FORMAT_INFO_DECODE_LOOKUP[$1B] := TArray.Create($3A06, $1B);
FORMAT_INFO_DECODE_LOOKUP[$1C] := TArray.Create($24B4, $1C);
FORMAT_INFO_DECODE_LOOKUP[$1D] := TArray.Create($2183, $1D);
FORMAT_INFO_DECODE_LOOKUP[30] := TArray.Create($2EDA, 30);
FORMAT_INFO_DECODE_LOOKUP[$1F] := TArray.Create($2BED, $1F);
BITS_SET_IN_HALF_BYTE := TArray.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.