Browse Source

sync changes with parent repo.

- update package version number for Lazarus/FPC
Ugochukwu Mmaduekwe 6 years ago
parent
commit
7de140ecd4

+ 1 - 1
QRCodeGenLib/src/Packages/FPC/QRCodeGenLib4PascalPackage.lpk

@@ -22,7 +22,7 @@
     <Description Value="QRCodeGenLib4Pascal is a Delphi/FPC compatible library that provides an easy to use interface for generating QR Codes.
 "/>
     <License Value="MIT License"/>
-    <Version Major="1" Minor="6"/>
+    <Version Major="1" Minor="7"/>
     <Files Count="18">
       <Item1>
         <Filename Value="..\..\QRCodeGen\QlpBitBuffer.pas"/>

+ 14 - 11
QRCodeGenLib/src/QRCodeGen/QlpBitBuffer.pas

@@ -19,13 +19,15 @@ resourcestring
 type
 
   /// <summary>
-  /// An appendable sequence of bits (0s and 1s). Mainly used by <see cref="QlpQrSegment">
+  /// // An appendable sequence of bits (0s and 1s), mainly used by <see cref="QlpQrSegment">
   /// TQrSegment</see>.
   /// </summary>
   TBitBuffer = record
   strict private
   var
+    // In each 32-bit word, bits are filled from top down.
     FData: TQRCodeGenLibInt32Array;
+    // Always non-negative.
     FBitLength: Int32;
     FBitBufferInitialized: Boolean;
 
@@ -43,21 +45,18 @@ type
   public
 
     /// <summary>
-    /// Constructs an empty bit buffer (length 0).
+    /// Creates an empty bit buffer.
     /// </summary>
     class function Create(): TBitBuffer; static;
 
     /// <summary>
-    /// Returns the length of this sequence, which is a non-negative value.
+    /// Returns the bit at the given index, yielding 0 or 1.
     /// </summary>
-    /// <returns>
-    /// the length of this sequence
-    /// </returns>
     function GetBit(AIndex: Int32): Int32; inline;
 
     /// <summary>
-    /// Returns an array representing this buffer's bits packed into bytes in
-    /// big endian. The current bit length must be a multiple of 8.
+    /// Returns a new array representing this buffer's bits packed into
+    /// bytes in big endian. The current bit length must be a multiple of 8.
     /// </summary>
     /// <returns>
     /// a new byte array representing this bit sequence
@@ -65,9 +64,8 @@ type
     function GetBytes(): TQRCodeGenLibByteArray; inline;
 
     /// <summary>
-    /// Appends the specified number of low-order bits of the specified value
-    /// to this buffer. Requires 0 ≤ len ≤ 31 and 0 ≤ val &lt; 2 <sup>len</sup>
-    /// .
+    /// Appends the given number of low-order bits of the given value to this
+    /// buffer. Requires 0 &lt;= len &lt;= 31 and 0 &lt;= val &lt; 2^len.
     /// </summary>
     /// <param name="AValue">
     /// the value to append
@@ -84,6 +82,11 @@ type
     /// </exception>
     procedure AppendBits(AValue, ALength: Int32); overload;
 
+    /// <summary>
+    /// Appends to this buffer the sequence of bits represented by the given
+    /// word array and given bit length. Requires 0 &lt;= len &lt;= 32 *
+    /// System.Length(vals).
+    /// </summary>
     procedure AppendBits(const AValues: TQRCodeGenLibInt32Array;
       ALength: Int32); overload;
 

+ 102 - 78
QRCodeGenLib/src/QRCodeGen/QlpQrCode.pas

@@ -1,4 +1,4 @@
-unit QlpQrCode;
+unit QlpQrCode;
 
 {$I ..\Include\QRCodeGenLib.inc}
 
@@ -171,8 +171,8 @@ type
     /// </summary>
     procedure SetModule(Ax, Ay, ABlack: Int32);
 
-    // Returns a new byte string representing the given data with the appropriate error correction
-    // codewords appended to it, based on this object's version and error correction level.
+    // Draws the given sequence of 8-bit codewords (data and error correction)
+    // onto the entire data area of this QR Code, based on the given bit indexes.
     function AddEccAndInterleave(const AData: TQRCodeGenLibByteArray)
       : TQRCodeGenLibByteArray;
 
@@ -185,11 +185,11 @@ type
     // The function modules must be marked and the codeword bits must be drawn
     // before masking. Due to the arithmetic of XOR, calling ApplyMask() with
     // the same mask value a second time will undo the mask. A final well-formed
-    // QR Code symbol needs exactly one (not zero, two, etc.) mask applied.
+    // QR Code needs exactly one (not zero, two, etc.) mask applied.
     procedure ApplyMask(const AMask: TQRCodeGenLibInt32Array);
 
     // A messy helper function for the constructor. This QR Code must be in an unmasked state when this
-    // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed.
+    // method is called. The 'mask' argument is the requested mask, which is -1 for auto or 0 to 7 for fixed.
     // This method applies and returns the actual mask chosen, from 0 to 7.
     function HandleConstructorMasking(const AMasks
       : TQRCodeGenLibMatrixInt32Array; AMask: Int32): Int32;
@@ -215,16 +215,18 @@ type
     class function GetNumDataCodeWords(AVersion: Int32; AEcl: TEcc)
       : Int32; inline;
 
-    // Inserts the given value to the front of the given array, which shifts over the
-    // existing values and deletes the last value. A helper function for GetPenaltyScore().
-    class procedure AddRunToHistory(ARun: Int32;
-      const AHistory: TQRCodeGenLibInt32Array); static; inline;
+    // Pushes the given value to the front and drops the last value. A helper function for GetPenaltyScore().
+    class procedure FinderPenaltyAddHistory(ACurrentRunLength: Int32;
+      const ARunHistory: TQRCodeGenLibInt32Array); static; inline;
 
-    // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and
-    // surrounded by at least 4 on either or both ends. A helper function for GetPenaltyScore().
-    // Must only be called immediately after a run of white modules has ended.
-    class function HasFinderLikePattern(const ARunHistory
-      : TQRCodeGenLibInt32Array): Boolean; static; inline;
+    // Can only be called immediately after a white run is added, and
+    // returns either 0, 1, or 2. A helper function for getPenaltyScore().
+    function FinderPenaltyCountPatterns(const ARunHistory
+      : TQRCodeGenLibInt32Array): Int32; inline;
+
+    // Must be called at the end of a line (row or column) of modules. A helper function for GetPenaltyScore()
+    function FinderPenaltyTerminateAndCount(ACurrentRunColor, ACurrentRunLength
+      : Int32; ARunHistory: TQRCodeGenLibInt32Array): Int32; inline;
 
 {$IFNDEF FMX}
     class function GetRValue(Argb: UInt32): Byte; static; inline;
@@ -657,8 +659,8 @@ begin
     LMinPenalty := System.High(Int32);
     for LIdx := 0 to System.Pred(8) do
     begin
-      DrawFormatBits(LIdx);
       ApplyMask(AMasks[LIdx]);
+      DrawFormatBits(LIdx);
       LPenalty := GetPenaltyScore();
       if (LPenalty < LMinPenalty) then
       begin
@@ -671,8 +673,8 @@ begin
 {$IFDEF DEBUG}
   System.Assert((0 <= AMask) and (AMask <= 7));
 {$ENDIF DEBUG}
-  DrawFormatBits(AMask); // Overwrite old format bits
   ApplyMask(AMasks[AMask]); // Apply the final choice of mask
+  DrawFormatBits(AMask); // Overwrite old format bits
   // The caller shall assign this value to the final-declared field
   Result := AMask;
 end;
@@ -702,29 +704,69 @@ begin
     [LEclInt][AVersion]);
 end;
 
-class function TQrCode.HasFinderLikePattern(const ARunHistory
-  : TQRCodeGenLibInt32Array): Boolean;
+class procedure TQrCode.FinderPenaltyAddHistory(ACurrentRunLength: Int32;
+  const ARunHistory: TQRCodeGenLibInt32Array);
+begin
+  System.Move(ARunHistory[0], ARunHistory[1], (System.Length(ARunHistory) - 1) *
+    System.SizeOf(Int32));
+  ARunHistory[0] := ACurrentRunLength;
+end;
+
+function TQrCode.FinderPenaltyCountPatterns(const ARunHistory
+  : TQRCodeGenLibInt32Array): Int32;
 var
-  Ln: Int32;
+  Ln, LTempA, LTempB: Int32;
+  LCore: Boolean;
 begin
   Ln := ARunHistory[1];
-  Result := (Ln > 0) and (ARunHistory[2] = Ln) and (ARunHistory[4] = Ln) and
-    (ARunHistory[5] = Ln) and (ARunHistory[3] = Ln * 3) and
-    (Max(ARunHistory[0], ARunHistory[6]) >= Ln * 4);
+{$IFDEF DEBUG}
+  System.Assert(Ln <= (Size * 3));
+{$ENDIF DEBUG}
+  LCore := (Ln > 0) and (ARunHistory[2] = Ln) and (ARunHistory[3] = Ln * 3) and
+    (ARunHistory[4] = Ln) and (ARunHistory[5] = Ln);
+
+  if ((LCore) and ((ARunHistory[0] >= (Ln * 4)) and (ARunHistory[6] >= Ln)))
+  then
+  begin
+    LTempA := 1;
+  end
+  else
+  begin
+    LTempA := 0;
+  end;
+
+  if ((LCore) and ((ARunHistory[6] >= (Ln * 4)) and (ARunHistory[0] >= Ln)))
+  then
+  begin
+    LTempB := 1;
+  end
+  else
+  begin
+    LTempB := 0;
+  end;
+
+  Result := LTempA + LTempB;
 end;
 
-class procedure TQrCode.AddRunToHistory(ARun: Int32;
-  const AHistory: TQRCodeGenLibInt32Array);
+function TQrCode.FinderPenaltyTerminateAndCount(ACurrentRunColor,
+  ACurrentRunLength: Int32; ARunHistory: TQRCodeGenLibInt32Array): Int32;
 begin
-  System.Move(AHistory[0], AHistory[1], (System.Length(AHistory) - 1) *
-    System.SizeOf(Int32));
-  AHistory[0] := ARun;
+  // Terminate black run
+  if (ACurrentRunColor = 1) then
+  begin
+    FinderPenaltyAddHistory(ACurrentRunLength, ARunHistory);
+    ACurrentRunLength := 0;
+  end;
+  ACurrentRunLength := ACurrentRunLength + Size;
+  // Add white border to final run
+  FinderPenaltyAddHistory(ACurrentRunLength, ARunHistory);
+  Result := FinderPenaltyCountPatterns(ARunHistory);
 end;
 
 function TQrCode.GetPenaltyScore: Int32;
 var
-  LEnd, LBlack, LIndex, LDownIndex, LCurRow, LNextRow, LColor, LRunX, LRunY, Lx,
-    Lc, LTotal, Lk, Ly: Int32;
+  LEnd, LBlack, LIndex, LDownIndex, LCurRow, LNextRow, LRunColor, LRunX, LRunY,
+    LPadRun, Lx, Lc, LTotal, Lk, Ly: Int32;
   LRunHistory: TQRCodeGenLibInt32Array;
 begin
   Result := 0;
@@ -737,10 +779,10 @@ begin
   // Iterate over adjacent pairs of rows
   while LIndex < LEnd do
   begin
-
-    TArrayUtils.Fill(LRunHistory, 0);
-    LColor := 0;
+    LRunColor := 0;
     LRunX := 0;
+    TArrayUtils.Fill(LRunHistory, 0);
+    LPadRun := Size; // Add white border to initial run
     LCurRow := 0;
     LNextRow := 0;
 
@@ -751,7 +793,7 @@ begin
       // Adjacent modules having same color
       Lc := TQrCodeCommons.GetBit(FModules[TBits.Asr32(LIndex, 5)], LIndex);
 
-      if (Lc = LColor) then
+      if (Lc = LRunColor) then
       begin
         System.Inc(LRunX);
         if (LRunX = 5) then
@@ -765,12 +807,14 @@ begin
       end
       else
       begin
-        AddRunToHistory(LRunX, LRunHistory);
-        if ((LColor = 0) and (HasFinderLikePattern(LRunHistory))) then
+        FinderPenaltyAddHistory(LRunX + LPadRun, LRunHistory);
+        LPadRun := 0;
+        if (LRunColor = 0) then
         begin
-          Result := Result + PENALTY_N3;
+          Result := Result + FinderPenaltyCountPatterns(LRunHistory) *
+            PENALTY_N3;
         end;
-        LColor := Lc;
+        LRunColor := Lc;
         LRunX := 1;
       end;
 
@@ -795,25 +839,18 @@ begin
       System.Inc(LDownIndex);
     end;
 
-    AddRunToHistory(LRunX, LRunHistory);
-    if (LColor = 1) then
-    begin
-      AddRunToHistory(0, LRunHistory); // Dummy run of white
-    end;
-    if (HasFinderLikePattern(LRunHistory)) then
-    begin
-      Result := Result + PENALTY_N3;
-    end;
-
+    Result := Result + FinderPenaltyTerminateAndCount(LRunColor,
+      LRunX + LPadRun, LRunHistory) * PENALTY_N3;
   end;
 
   // Iterate over single columns
   Lx := 0;
   while Lx < FSize do
   begin
-    TArrayUtils.Fill(LRunHistory, 0);
-    LColor := 0;
+    LRunColor := 0;
     LRunY := 0;
+    TArrayUtils.Fill(LRunHistory, 0);
+    LPadRun := Size; // Add white border to initial run
     Ly := 0;
     LIndex := Lx;
     while Ly < FSize do
@@ -821,7 +858,7 @@ begin
       // Adjacent modules having same color
       Lc := TQrCodeCommons.GetBit(FModules[TBits.Asr32(LIndex, 5)], LIndex);
 
-      if (Lc = LColor) then
+      if (Lc = LRunColor) then
       begin
         System.Inc(LRunY);
         if (LRunY = 5) then
@@ -835,27 +872,22 @@ begin
       end
       else
       begin
-        AddRunToHistory(LRunY, LRunHistory);
-        if ((LColor = 0) and (HasFinderLikePattern(LRunHistory))) then
+        FinderPenaltyAddHistory(LRunY + LPadRun, LRunHistory);
+        LPadRun := 0;
+        if (LRunColor = 0) then
         begin
-          Result := Result + PENALTY_N3;
+          Result := Result + FinderPenaltyCountPatterns(LRunHistory) *
+            PENALTY_N3;
         end;
-        LColor := Lc;
+        LRunColor := Lc;
         LRunY := 1;
       end;
 
       System.Inc(Ly);
       System.Inc(LIndex, FSize);
     end;
-    AddRunToHistory(LRunY, LRunHistory);
-    if (LColor = 1) then
-    begin
-      AddRunToHistory(0, LRunHistory); // Dummy run of white
-    end;
-    if (HasFinderLikePattern(LRunHistory)) then
-    begin
-      Result := Result + PENALTY_N3;
-    end;
+    Result := Result + FinderPenaltyTerminateAndCount(LRunColor,
+      LRunY + LPadRun, LRunHistory) * PENALTY_N3;
     System.Inc(Lx);
   end;
 
@@ -1010,22 +1042,15 @@ begin
 {$IFDEF DEBUG}
   System.Assert((0 <= Ax) and (Ax < FSize));
   System.Assert((0 <= Ay) and (Ay < FSize));
+  System.Assert((ABlack = 0) or (ABlack = 1));
 {$ENDIF DEBUG}
   LIdx := (Ay * FSize) + Ax;
-  if (ABlack = 0) then
-  begin
-    FModules[TBits.Asr32(LIdx, 5)] := FModules[TBits.Asr32(LIdx, 5)] and
-      (not(TBits.LeftShift32(1, LIdx)));
-  end
-  else if (ABlack = 1) then
-  begin
-    FModules[TBits.Asr32(LIdx, 5)] := FModules[TBits.Asr32(LIdx, 5)] or
-      (TBits.LeftShift32(1, LIdx));
-  end
-  else
-  begin
-    raise EArgumentInvalidQRCodeGenLibException.Create('');
-  end;
+
+  FModules[TBits.Asr32(LIdx, 5)] := FModules[TBits.Asr32(LIdx, 5)] and
+    (not(TBits.LeftShift32(1, LIdx)));
+
+  FModules[TBits.Asr32(LIdx, 5)] := FModules[TBits.Asr32(LIdx, 5)] or
+    (TBits.LeftShift32(ABlack, LIdx));
 end;
 
 {$IFDEF LCL}
@@ -1494,7 +1519,6 @@ begin
 
   // Create the QR Code symbol
   Result := TQrCode.Create(LVersion, AEcl, LBitBuffer.GetBytes(), AMask);
-
 end;
 
 class function TQrCode.EncodeText(const AText: String; AEcl: TEcc;

+ 14 - 3
QRCodeGenLib/src/QRCodeGen/QlpQrTemplate.pas

@@ -30,16 +30,23 @@ type
     FLock: TCriticalSection;
 
   var
-    FVersion, FSize: Int32;
+    FVersion: Int32; // In the range [1, 40].
+    FSize: Int32; // Derived from version.
 
     // "FIsFunction" Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
-    FTemplate, FDataOutputBitIndexes, FIsFunction: TQRCodeGenLibInt32Array;
+    // Otherwise when the constructor is running, System.length(FIsFunction) == System.length(FTemplate).
+    FIsFunction: TQRCodeGenLibInt32Array;
+    FTemplate: TQRCodeGenLibInt32Array;
+    // Length and values depend on version.
+    FDataOutputBitIndexes: TQRCodeGenLibInt32Array;
+    // System.length(FMasks) == 8, and System.length(FMasks[i]) == System.length(FTemplate).
     FMasks: TQRCodeGenLibMatrixInt32Array;
 
     function GetTemplate(): TQRCodeGenLibInt32Array; inline;
     function GetDataOutputBitIndexes(): TQRCodeGenLibInt32Array; inline;
     function GetMasks(): TQRCodeGenLibMatrixInt32Array;
 
+    // Returns the value of the bit at the given coordinates in the given grid.
     function GetModule(const AGrid: TQRCodeGenLibInt32Array; Ax, Ay: Int32)
       : Int32; inline;
 
@@ -48,10 +55,13 @@ type
     // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
     function GetAlignmentPatternPositions(): TQRCodeGenLibInt32Array;
 
+    // Computes and returns an array of bit indexes, based on this object's various fields.
     function GenerateZigZagScan(): TQRCodeGenLibInt32Array;
 
+    // Computes and returns a new array of masks, based on this object's various fields.
     function GenerateMasks(): TQRCodeGenLibMatrixInt32Array;
 
+    // Reads this object's version field, and draws and marks all function modules.
     procedure DrawFunctionPatterns();
     // Draws two blank copies of the format bits.
     procedure DrawDummyFormatBits();
@@ -68,6 +78,7 @@ type
     // Also either sets that module black or keeps its color unchanged.
     procedure DarkenFunctionModule(Ax, Ay, AEnable: Int32); inline;
 
+    // Creates a QR Code template for the given version number.
     constructor Create(AVersion: Int32);
 
     class constructor CreateQrTemplate();
@@ -230,7 +241,7 @@ begin
   begin
     DarkenFunctionModule(8, FSize - 15 + LIdx, 0);
   end;
-  DarkenFunctionModule(8, FSize - 8, 1);
+  DarkenFunctionModule(8, FSize - 8, 1); // Always black
 end;
 
 procedure TQrTemplate.DrawFinderPattern(Ax, Ay: Int32);

+ 9 - 7
QRCodeGenLib/src/QRCodeGen/QlpReedSolomonGenerator.pas

@@ -28,11 +28,10 @@ type
 
   var
     // A table of size 256 * degree, where FPolynomialMultiply[i][j] := Multiply(i, coefficients[j]).
-    // 'coefficients' is the temporary array representing the coefficients of the divisor polynomial,
-    // stored from highest to lowest power, excluding the leading term which is always 1.
-    // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
+    // 'coefficients' is the temporary array computed in the constructor.
     FPolynomialMultiply: TQRCodeGenLibMatrixByteArray;
 
+    // Creates a Reed-Solomon ECC generator polynomial for the given degree.
     constructor Create(ADegree: Int32);
     // Returns the product of the two given field elements modulo GF(2^8/$11D). The arguments and result
     // are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8.
@@ -42,6 +41,7 @@ type
     class destructor DestroyReedSolomonGenerator();
 
   public
+    // Returns the error correction codeword for the given data polynomial and this divisor polynomial.
     procedure GetRemainder(const AData: TQRCodeGenLibByteArray;
       ADataOff, ADataLen: Int32; const AResult: TQRCodeGenLibByteArray);
     class function GetInstance(ADegree: Int32): IReedSolomonGenerator; static;
@@ -64,12 +64,14 @@ begin
       (@SDegreeOutOfRange);
   end;
 
-  // Start with the monomial x^0
+  // The divisor polynomial, whose coefficients are stored from highest to lowest power.
+  // For example, x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
   System.SetLength(LCoefficients, ADegree);
+  // Start off with the monomial x^0
   LCoefficients[ADegree - 1] := 1;
 
   // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
-  // drop the highest term, and store the rest of the coefficients in order of descending powers.
+  // and drop the highest monomial term which is always 1x^degree.
   // Note that r = $02, which is a generator element of this field GF(2^8/$11D).
   LRoot := 1;
   LIIdx := 0;
@@ -181,10 +183,10 @@ begin
 {$IFDEF DEBUG}
   System.Assert(System.Length(AResult) = LDegree);
 {$ENDIF DEBUG}
-  // Compute the remainder by performing polynomial division
   TArrayUtils.Fill(AResult, Byte(0));
   LIIdx := ADataOff;
   LDataEnd := ADataOff + ADataLen;
+  // Polynomial division
   while LIIdx < LDataEnd do
   begin
     LTable := FPolynomialMultiply[(AData[LIIdx] xor AResult[0]) and $FF];
@@ -205,7 +207,7 @@ var
   Lz, LIdx: Int32;
 begin
 {$IFDEF DEBUG}
-  System.Assert((TBits.Asr32(Ax, 8) = 0) and (TBits.Asr32(Ay, 8) = 0));
+  System.Assert(((Ax shr 8) = 0) and ((Ay shr 8) = 0));
 {$ENDIF DEBUG}
   // Russian peasant multiplication
   Lz := 0;