1. 程式人生 > >Delphi xe7 FireMonkey / Mobile (Android, iOS)生成 QR Code完整例項

Delphi xe7 FireMonkey / Mobile (Android, iOS)生成 QR Code完整例項

這個例項在windows、OS X、IOS和Android等平臺執行正常。
本文參考這個網站提供的方法:http://zarko-gajic.iz.hr/firemonkey-mobile-android-ios-qr-code-generation-using-delphi-xe-5-delphizxingqrcode/

程式碼中用到的DelphiZXingQRCode.Pas點這下載

複製程式碼
  1 unit Unit3;
  2 
  3 interface
  4 
  5 uses
  6   System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  7   FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects,
  8   FMX.Controls.Presentation, FMX.Edit, FMX.StdCtrls,DelphiZXingQRCode,
  9   FMX.ListBox,system.math;
 10 
 11 type
 12   TForm3 = class(TForm)
 13     Button1: TButton;
 14     edtText: TEdit;
 15     imgQRCode: TImage;
 16     cmbEncoding: TComboBox;
 17     edtQuietZone: TEdit;
 18     procedure Button1Click(Sender: TObject);
 19   private
 20     { Private declarations }
 21     BMP: TBitmap;
 22   public
 23     { Public declarations }
 24   end;
 25 
 26 var
 27   Form3: TForm3;
 28 
 29 implementation
 30 
 31 {$R *.fmx}
 32 
 33 procedure TForm3.Button1Click(Sender: TObject);
 34 const
 35   downsizeQuality: Integer = 2; // bigger value, better quality, slower rendering
 36 var
 37   QRCode: TDelphiZXingQRCode;
 38   Row, Column: Integer;
 39   pixelColor : TAlphaColor;
 40   vBitMapData : TBitmapData;
 41   pixelCount, y, x: Integer;
 42   columnPixel, rowPixel: Integer;
 43   function GetPixelCount(AWidth, AHeight: Single): Integer;
 44   begin
 45     if QRCode.Rows > 0 then
 46       Result := Trunc(Min(AWidth, AHeight)) div QRCode.Rows
 47     else
 48       Result := 0;
 49   end;
 50 begin
 51   QRCode := TDelphiZXingQRCode.Create;
 52   try
 53     QRCode.Data := edtText.Text;
 54     QRCode.Encoding := TQRCodeEncoding(cmbEncoding.ItemIndex);
 55     QRCode.QuietZone := StrToIntDef(edtQuietZone.Text, 4);
 56     pixelCount := GetPixelCount(imgQRCode.Width, imgQRCode.Height);
 57     case imgQRCode.WrapMode of
 58       TImageWrapMode.iwOriginal,TImageWrapMode.iwTile,TImageWrapMode.iwCenter:
 59       begin
 60         if pixelCount > 0 then
 61           imgQRCode.Bitmap.SetSize(QRCode.Columns * pixelCount,
 62             QRCode.Rows * pixelCount);
 63       end;
 64       TImageWrapMode.iwFit:
 65       begin
 66         if pixelCount > 0 then
 67         begin
 68           imgQRCode.Bitmap.SetSize(QRCode.Columns * pixelCount * downsizeQuality,
 69             QRCode.Rows * pixelCount * downsizeQuality);
 70           pixelCount := pixelCount * downsizeQuality;
 71         end;
 72       end;
 73       TImageWrapMode.iwStretch:
 74         raise Exception.Create('Not a good idea to stretch the QR Code');
 75     end;
 76//     if imgQRCode.Bitmap.Canvas.BeginScene then
 77//     begin
 78       try
 79         imgQRCode.Bitmap.Canvas.Clear(TAlphaColors.White);
 80         if pixelCount > 0 then
 81         begin
 82           if imgQRCode.Bitmap.Map(TMapAccess.maWrite, vBitMapData)  then
 83           begin
 84             try
 85               for Row := 0 to QRCode.Rows - 1 do
 86               begin
 87                 for Column := 0 to QRCode.Columns - 1 do
 88                 begin
 89                   if (QRCode.IsBlack[Row, Column]) then
 90                     pixelColor := TAlphaColors.Black
 91                   else
 92                     pixelColor := TAlphaColors.White;
 93                   columnPixel := Column * pixelCount;
 94                   rowPixel := Row * pixelCount;
 95                   for x := 0 to pixelCount - 1 do
 96                     for y := 0 to pixelCount - 1 do
 97                       vBitMapData.SetPixel(columnPixel + x,
 98                         rowPixel + y, pixelColor);
 99                 end;
100               end;
101             finally
102               imgQRCode.Bitmap.Unmap(vBitMapData);
103             end;
104           end;
105         end;
106       finally
107//         imgQRCode.Bitmap.Canvas.EndScene;
108//       end;
109     end;
110   finally
111     QRCode.Free;
112   end;
113 end;
114 
115 end.
複製程式碼

FMX:

複製程式碼
 1 object Form3: TForm3
 2   Left = 0
 3   Top = 0
 4   Caption = 'Form3'
 5   ClientHeight = 487
 6   ClientWidth = 328
 7   FormFactor.Width = 320
 8   FormFactor.Height = 480
 9   FormFactor.Devices = [Desktop]
10   DesignerMasterStyle = 3
11   object Button1: TButton
12     Position.X = 32.000000000000000000
13     Position.Y = 104.000000000000000000
14     Size.Width = 89.000000000000000000
15     Size.Height = 44.000000000000000000
16     Size.PlatformDefault = False
17     TabOrder = 0
18     Text = 'Button1'
19     OnClick = Button1Click
20   end
21   object edtText: TEdit
22     Touch.InteractiveGestures = [LongTap, DoubleTap]
23     TabOrder = 1
24     Position.X = 32.000000000000000000
25     Position.Y = 56.000000000000000000
26     Size.Width = 233.000000000000000000
27     Size.Height = 32.000000000000000000
28     Size.PlatformDefault = False
29   end
30   object imgQRCode: TImage
31     MultiResBitmap = <
32       item
33       end>
34     Anchors = [akLeft, akTop, akRight, akBottom]
35     MarginWrapMode = Center
36     Position.X = 32.000000000000000000
37     Position.Y = 192.000000000000000000
38     Size.Width = 250.000000000000000000
39     Size.Height = 250.000000000000000000
40     Size.PlatformDefault = False
41   end
42   object cmbEncoding: TComboBox
43     Items.Strings = (
44       'Auto'
45       'Numeric'
46       'Alphanumeric'
47       'ISO-8859-1'
48       'UTF-8 without BOM'
49       'UTF-8 with BOM')
50     ItemIndex = 0
51     Position.X = 136.000000000000000000
52     Position.Y = 112.000000000000000000
53     Size.Width = 145.000000000000000000
54     Size.Height = 32.000000000000000000
55     Size.PlatformDefault = False
56     TabOrder = 3
57   end
58   object edtQuietZone: TEdit
59     Touch.InteractiveGestures = [LongTap, DoubleTap]
60     TabOrder = 4
61     Text = '4'
62     Position.X = 32.000000000000000000
63     Position.Y = 152.000000000000000000
64     Size.Width = 100.000000000000000000
65     Size.Height = 32.000000000000000000
66     Size.PlatformDefault = False
67   end
68 end
複製程式碼

 

2015-02-13 新的demo,簡化呼叫方式,要配合下面的DelphiZXIngQRCode.pas

  View Code

 

新的DelphiZXIngQRCode.pas

複製程式碼
unit DelphiZXIngQRCode;

// ZXing QRCode port to Delphi, by Debenu Pty Ltd
// www.debenu.com

// Original copyright notice
(*
  * 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.
*)


interface

uses
  System.UITypes,
  FMX.Graphics,
  FMX.Objects,
  FMX.Types;

type
  TQRCodeEncoding = (qrAuto, qrNumeric, qrAlphanumeric, qrISO88591, qrUTF8NoBOM,
    qrUTF8BOM);
  T2DBooleanArray = array of array of Boolean;

  TDelphiZXingQRCode = class
  protected
    FData: String;
    FRows: Integer;
    FColumns: Integer;
    FEncoding: TQRCodeEncoding;
    FQuietZone: Integer;
    FElements: T2DBooleanArray;
    procedure SetEncoding(NewEncoding: TQRCodeEncoding);
    procedure SetData(const NewData: string);
    procedure SetQuietZone(NewQuietZone: Integer);
    function GetIsBlack(Row, Column: Integer): Boolean;
    procedure Update;
  public
    constructor Create;
    procedure DrawQrcode(imgQRCode: TImage; QRCode: TDelphiZXingQRCode);
    property Data: string read FData write SetData;
    property Encoding: TQRCodeEncoding read FEncoding write SetEncoding;
    property QuietZone: Integer read FQuietZone write SetQuietZone;
    property Rows: Integer read FRows;
    property Columns: Integer read FColumns;
    property IsBlack[Row, Column: Integer]: Boolean read GetIsBlack;
  end;

implementation

uses
  System.Generics.Collections, Math, Classes, System.SysUtils;

type
  TByteArray = array of Byte;
  T2DByteArray = array of array of Byte;
  TIntegerArray = array of Integer;

const
  NUM_MASK_PATTERNS = 8;

  QUIET_ZONE_SIZE = 4;

  ALPHANUMERIC_TABLE: array [0 .. 95] of Integer = (-1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
    36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1 // 0x50-0x5f
    );

  DEFAULT_BYTE_MODE_ENCODING = 'ISO-8859-1';

  POSITION_DETECTION_PATTERN: array [0 .. 6, 0 .. 6] of Integer =
    ((1, 1, 1, 1, 1, 1, 1), (1, 0, 0, 0, 0, 0, 1), (1, 0, 1, 1, 1, 0, 1),
    (1, 0, 1, 1, 1, 0, 1), (1, 0, 1, 1, 1, 0, 1), (1, 0, 0, 0, 0, 0, 1),
    (1, 1, 1, 1, 1, 1, 1));

  HORIZONTAL_SEPARATION_PATTERN: array [0 .. 0, 0 .. 7] of Integer =
    ((0, 0, 0, 0, 0, 0, 0, 0));

  VERTICAL_SEPARATION_PATTERN: array [0 .. 6, 0 .. 0] of Integer = ((0), (0),
    (0), (0), (0), (0), (0));

  POSITION_ADJUSTMENT_PATTERN: array [0 .. 4, 0 .. 4] of Integer =
    ((1, 1, 1, 1, 1), (1, 0, 0, 0, 1), (1, 0, 1, 0, 1), (1, 0, 0, 0, 1),
    (1, 1, 1, 1, 1));

  // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.
  POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE: array [0 .. 39, 0 .. 6]
    of Integer = ((-1, -1, -1, -1, -1, -1, -1), // Version 1
    (6, 18, -1, -1, -1, -1, -1), // Version 2
    (6, 22, -1, -1, -1, -1, -1), // Version 3
    (6, 26, -1, -1, -1, -1, -1), // Version 4
    (6, 30, -1, -1, -1, -1, -1), // Version 5
    (6, 34, -1, -1, -1, -1, -1), // Version 6
    (6, 22, 38, -1, -1, -1, -1), // Version 7
    (6, 24, 42, -1, -1, -1, -1), // Version 8
    (6, 26, 46, -1, -1, -1, -1), // Version 9
    (6, 28, 50, -1, -1, -1, -1), // Version 10
    (6, 30, 54, -1, -1, -1, -1), // Version 11
    (6, 32, 58, -1, -1, -1, -1), // Version 12
    (6, 34, 62, -1, -1, -1, -1), // Version 13
    (6, 26, 46, 66, -1, -1, -1), // Version 14
    (6, 26, 48, 70, -1, -1, -1), // Version 15
    (6, 26, 50, 74, -1, -1, -1), // Version 16
    (6, 30, 54, 78, -1, -1, -1), // Version 17
    (6, 30, 56, 82, -1, -1, -1), // Version 18
    (6, 30, 58, 86, -1, -1, -1), // Version 19
    (6, 34, 62, 90, -1, -1, -1), // Version 20
    (6, 28, 50, 72, 94, -1, -1), // Version 21
    (6, 26, 50, 74, 98, -1, -1), // Version 22
    (6, 30, 54, 78, 102, -1, -1), // Version 23
    (6, 28, 54, 80, 106, -1, -1), // Version 24
    (6, 32, 58, 84, 110, -1, -1), // Version 25
    (6, 30, 58, 86, 114, -1, -1), // Version 26
    (6, 34, 62, 90, 118, -1, -1), // Version 27
    (6, 26, 50, 74, 98, 122, -1), // Version 28
    (6, 30, 54, 78, 102, 126, -1), // Version 29
    (6, 26, 52, 78, 104, 130, -1), // Version 30
    (6, 30, 56, 82, 108, 134, -1), // Version 31
    (6, 34, 60, 86, 112, 138, -1), // Version 32
    (6, 30, 58, 86, 114, 142, -1), // Version 33
    (6, 34, 62, 90, 118, 146, -1), // Version 34
    (6, 30, 54, 78, 102, 126, 150), // Version 35
    (6, 24, 50, 76, 102, 128, 154), // Version 36
    (6, 28, 54, 80, 106, 132, 158), // Version 37
    (6, 32, 58, 84, 110, 136, 162), // Version 38
    (6, 26, 54, 82, 110, 138, 166), // Version 39
    (6, 30, 58, 86, 114, 142, 170) // Version 40
    );

  // Type info cells at the left top corner.
  TYPE_INFO_COORDINATES: array [0 .. 14, 0 .. 1] of Integer = ((8, 0), (8, 1),
    (8, 2), (8, 3), (8, 4), (8, 5), (8, 7), (8, 8), (7, 8), (5, 8), (4, 8),
    (3, 8), (2, 8), (1, 8), (0, 8));

  // From Appendix D in JISX0510:2004 (p. 67)
  VERSION_INFO_POLY = $1F25; // 1 1111 0010 0101

  // From Appendix C in JISX0510:2004 (p.65).
  TYPE_INFO_POLY = $537;
  TYPE_INFO_MASK_PATTERN = $5412;

  VERSION_DECODE_INFO: array [0 .. 33] of Integer = (

    $07C94, $085BC, $09A99, $0A4D3, $0BBF6, $0C762, $0D847, $0E60D, $0F928,
    $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);

type
  TMode = (qmTerminator, qmNumeric, qmAlphanumeric, qmStructuredAppend, qmByte,
    qmECI, qmKanji, qmFNC1FirstPosition, qmFNC1SecondPosition, qmHanzi);

const
  ModeCharacterCountBits: array [TMode] of array [0 .. 2] of Integer =
    ((0, 0, 0), (10, 12, 14), (9, 11, 13), (0, 0, 0), (8, 16, 16), (0, 0, 0),
    (8, 10, 12), (0, 0, 0), (0, 0, 0), (8, 10, 12));

  ModeBits: array [TMode] of Integer = (0, 1, 2, 3, 4, 7, 8, 5, 9, 13);

type
  TErrorCorrectionLevel = class
  private
    FBits: Integer;
  public
    procedure Assign(Source: TErrorCorrectionLevel);
    function Ordinal: Integer;
    property Bits: Integer read FBits;
  end;

  TECB = class
  private
    Count: Integer;
    DataCodewords: Integer;
  public
    constructor Create(Count, DataCodewords: Integer);
    function GetCount: Integer;
    function GetDataCodewords: Integer;
  end;

  TECBArray = array of TECB;

  TECBlocks = class
  private
    ECCodewordsPerBlock: Integer;
    ECBlocks: TECBArray;
  public
    constructor Create(ECCodewordsPerBlock: Integer; ECBlocks: TECB); overload;
    constructor Create(ECCodewordsPerBlock: Integer;
      ECBlocks1, ECBlocks2: TECB); overload;
    destructor Destroy; override;
    function GetTotalECCodewords: Integer;
    function GetNumBlocks: Integer;
    function GetECCodewordsPerBlock: Integer;
    function GetECBlocks: TECBArray;
  end;

  TByteMatrix = class
  protected
    Bytes: T2DByteArray;
    FWidth: Integer;
    FHeight: Integer;
  public
    constructor Create(Width, Height: Integer);
    function Get(X, Y: Integer): Integer;
    procedure SetBoolean(X, Y: Integer; Value: Boolean);
    procedure SetInteger(X, Y: Integer; Value: Integer);
    function GetArray: T2DByteArray;
    procedure Assign(Source: TByteMatrix);
    procedure Clear(Value: Byte);
    function Hash: string;
    property Width: Integer read FWidth;
    property Height: Integer read FHeight;
  end;

  TBitArray = class
  private
    Bits: array of Integer;
    Size: Integer;
    procedure EnsureCapacity(Size: Integer);
  public
    constructor Create; overload;
    constructor Create(Size: Integer); overload;
    function GetSizeInBytes: Integer;
    function GetSize: Integer;
    function Get(I: Integer): Boolean;
    procedure SetBit(Index: Integer);
    procedure AppendBit(Bit: Boolean);
    procedure AppendBits(Value, NumBits: Integer);
    procedure AppendBitArray(NewBitArray: TBitArray);
    procedure ToBytes(BitOffset: Integer; Source: TByteArray;
      Offset, NumBytes: Integer);
    procedure XorOperation(Other: TBitArray);
  end;

  TCharacterSetECI = class

  end;

  TVersion = class
  private
    VersionNumber: Integer;
    AlignmentPatternCenters: array of Integer;
    ECBlocks: array of TECBlocks;
    TotalCodewords: Integer;
    ECCodewords: Integer;
  public
    constructor Create(VersionNumber: Integer;
      AlignmentPatternCenters: array of Integer;
      ECBlocks1, ECBlocks2, ECBlocks3, ECBlocks4: TECBlocks);
    destructor Destroy; override;
    class function GetVersionForNumber(VersionNum: Integer): TVersion;
    class function ChooseVersion(NumInputBits: Integer;
      ecLevel: TErrorCorrectionLevel): TVersion;
    function GetTotalCodewords: Integer;
    function GetECBlocksForLevel(ecLevel: TErrorCorrectionLevel): TECBlocks;
    function GetDimensionForVersion: Integer;
  end;

  TMaskUtil = class
  public
    function GetDataMaskBit(MaskPattern, X, Y: Integer): Boolean;
  end;

  TQRCode = class
  private
    FMode: TMode;
    FECLevel: TErrorCorrectionLevel;
    FVersion: Integer;
    FMatrixWidth: Integer;
    FMaskPattern: Integer;
    FNumTotalBytes: Integer;
    FNumDataBytes: Integer;
    FNumECBytes: Integer;
    FNumRSBlocks: Integer;
    FMatrix: TByteMatrix;
    FQRCodeError: Boolean;
  public
    constructor Create;
    destructor Destroy; override;
    function At(X, Y: Integer): Integer;
    function IsValid: Boolean;
    function IsValidMaskPattern(MaskPattern: Integer): Boolean;
    procedure SetMatrix(NewMatrix: TByteMatrix);
    procedure SetECLevel(NewECLevel: TErrorCorrectionLevel);
    procedure SetAll(VersionNum, NumBytes, NumDataBytes, NumRSBlocks,
      NumECBytes, MatrixWidth: Integer);
    property QRCodeError: Boolean read FQRCodeError;
    property Mode: TMode read FMode write FMode;
    property Version: Integer read FVersion write FVersion;
    property NumDataBytes: Integer read FNumDataBytes;
    property NumTotalBytes: Integer read FNumTotalBytes;
    property NumRSBlocks: Integer read FNumRSBlocks;
    property MatrixWidth: Integer read FMatrixWidth;
    property MaskPattern: Integer read FMaskPattern write FMaskPattern;
    property ecLevel: TErrorCorrectionLevel read FECLevel;
  end;

  TMatrixUtil = class

  private
    FMatrixUtilError: Boolean;
    procedure ClearMatrix(Matrix: TByteMatrix);

    procedure EmbedBasicPatterns(Version: Integer; Matrix: TByteMatrix);
    procedure EmbedTypeInfo(ecLevel: TErrorCorrectionLevel;
      MaskPattern: Integer; Matrix: TByteMatrix);
    procedure MaybeEmbedVersionInfo(Version: Integer; Matrix: TByteMatrix);
    procedure EmbedDataBits(DataBits: TBitArray; MaskPattern: Integer;
      Matrix: TByteMatrix);
    function FindMSBSet(Value: Integer): Integer;
    function CalculateBCHCode(Value, Poly: Integer): Integer;
    procedure MakeTypeInfoBits(ecLevel: TErrorCorrectionLevel;
      MaskPattern: Integer; Bits: TBitArray);
    procedure MakeVersionInfoBits(Version: Integer; Bits: TBitArray);
    function IsEmpty(Value: Integer): Boolean;
    procedure EmbedTimingPatterns(Matrix: TByteMatrix);
    procedure EmbedDarkDotAtLeftBottomCorner(Matrix: TByteMatrix);
    procedure EmbedHorizontalSeparationPattern(XStart, YStart: Integer;
      Matrix: TByteMatrix);
    procedure EmbedVerticalSeparationPattern(XStart, YStart: Integer;
      Matrix: TByteMatrix);
    procedure EmbedPositionAdjustmentPattern(XStart, YStart: Integer;
      Matrix: TByteMatrix);
    procedure EmbedPositionDetectionPattern(XStart, YStart: Integer;
      Matrix: TByteMatrix);
    procedure EmbedPositionDetectionPatternsAndSeparators(Matrix: TByteMatrix);
    procedure MaybeEmbedPositionAdjustmentPatterns(Version: Integer;
      Matrix: TByteMatrix);
  public
    constructor Create;
    property MatrixUtilError: Boolean read FMatrixUtilError;
    procedure BuildMatrix(DataBits: TBitArray; ecLevel: TErrorCorrectionLevel;
      Version, MaskPattern: Integer; Matrix: TByteMatrix);
  end;

function GetModeBits(Mode: TMode): Integer;
begin
  Result := ModeBits[Mode];
end;

function GetModeCharacterCountBits(Mode: TMode; Version: TVersion): Integer;
var
  Number: Integer;
  Offset: Integer;
begin
  Number := Version.VersionNumber;

  if (Number <= 9) then
  begin
    Offset := 0;
  end
  else if (Number <= 26) then
  begin
    Offset := 1;
  end
  else
  begin
    Offset := 2;
  end;
  Result := ModeCharacterCountBits[Mode][Offset];
end;

type
  TBlockPair = class
  private
    FDataBytes: TByteArray;
    FErrorCorrectionBytes: TByteArray;
  public
    constructor Create(BA1, BA2: TByteArray);
    function GetDataBytes: TByteArray;
    function GetErrorCorrectionBytes: TByteArray;
  end;

  TGenericGFPoly = class;

  TGenericGF = class
  private
    FExpTable: TIntegerArray;
    FLogTable: TIntegerArray;
    FZero: TGenericGFPoly;
    FOne: TGenericGFPoly;
    FSize: Integer;
    FPrimitive: Integer;
    FGeneratorBase: Integer;
    FInitialized: Boolean;
    FPolyList: array of TGenericGFPoly;

    procedure CheckInit;
    procedure Initialize;
  public
    class function CreateQRCodeField256: TGenericGF;
    class function AddOrSubtract(A, B: Integer): Integer;
    constructor Create(Primitive, Size, B: Integer);
    destructor Destroy; override;
    function GetZero: TGenericGFPoly;
    function Exp(A: Integer): Integer;
    function GetGeneratorBase: Integer;
    function Inverse(A: Integer): Integer;
    function Multiply(A, B: Integer): Integer;
    function BuildMonomial(Degree, Coefficient: Integer): TGenericGFPoly;
  end;

  TGenericGFPolyArray = array of TGenericGFPoly;

  TGenericGFPoly = class
  private
    FField: TGenericGF;
    FCoefficients: TIntegerArray;
  public
    constructor Create(AField: TGenericGF; ACoefficients: TIntegerArray);
    destructor Destroy; override;
    function Coefficients: TIntegerArray;
    function Multiply(Other: TGenericGFPoly): TGenericGFPoly;
    function MultiplyByMonomial(Degree, Coefficient: Integer): TGenericGFPoly;
    function Divide(Other: TGenericGFPoly): TGenericGFPolyArray;
    function GetCoefficients: TIntegerArray;
    function IsZero: Boolean;
    function GetCoefficient(Degree: Integer): Integer;
    function GetDegree: Integer;
    function AddOrSubtract(Other: TGenericGFPoly): TGenericGFPoly;
  end;

  TReedSolomonEncoder = class
  private
    FField: TGenericGF;
    FCachedGenerators: TObjectList<TGenericGFPoly>;
  public
    constructor Create(AField: TGenericGF);
    destructor Destroy; override;
    procedure Encode(ToEncode: TIntegerArray; ECBytes: Integer);
    function BuildGenerator(Degree: Integer): TGenericGFPoly;
  end;

  TEncoder = class
  private
    FEncoderError: Boolean;

    function ApplyMaskPenaltyRule1Internal(Matrix: TByteMatrix;
      IsHorizontal: Boolean): Integer;
    function ChooseMode(const Content: string; var EncodeOptions: Integer)
      : TMode; overload;
    function FilterContent(const Content: string; Mode: TMode;
      EncodeOptions: Integer): string;
    procedure Append8BitBytes(const Content: string; Bits: TBitArray;
      EncodeOptions: Integer);

    procedure AppendAlphanumericBytes(const Content: string; Bits: TBitArray);
    procedure AppendBytes(const Content: string; Mode: TMode; Bits: TBitArray;
      EncodeOptions: Integer);
    procedure AppendKanjiBytes(const Content: string; Bits: TBitArray);
    procedure AppendLengthInfo(NumLetters, VersionNum: Integer; Mode: TMode;
      Bits: TBitArray);
    procedure AppendModeInfo(Mode: TMode; Bits: TBitArray);
    procedure AppendNumericBytes(const Content: string; Bits: TBitArray);
    function ChooseMaskPattern(Bits: TBitArray; ecLevel: TErrorCorrectionLevel;
      Version: Integer; Matrix: TByteMatrix): Integer;
    function GenerateECBytes(DataBytes: TByteArray;

      NumECBytesInBlock: Integer): TByteArray;
    function GetAlphanumericCode(Code: Integer): Integer;
    procedure GetNumDataBytesAndNumECBytesForBlockID(NumTotalBytes,
      NumDataBytes, NumRSBlocks, BlockID: Integer;
      var NumDataBytesInBlock: TIntegerArray;
      var NumECBytesInBlock: TIntegerArray);
    procedure InterleaveWithECBytes(Bits: TBitArray;
      NumTotalBytes, NumDataBytes, NumRSBlocks: Integer; var Result: TBitArray);
    // function IsOnlyDoubleByteKanji(const Content: string): Boolean;
    procedure TerminateBits(NumDataBytes: Integer; var Bits: TBitArray);
    function CalculateMaskPenalty(Matrix: TByteMatrix): Integer;
    function ApplyMaskPenaltyRule1(Matrix: TByteMatrix): Integer;
    function ApplyMaskPenaltyRule2(Matrix: TByteMatrix): Integer;
    function ApplyMaskPenaltyRule3(Matrix: TByteMatrix): Integer;
    function ApplyMaskPenaltyRule4(Matrix: TByteMatrix): Integer;
    // procedure Encode(const Content: string; ECLevel: TErrorCorrectionLevel; QRCode: TQRCode); overload;
    procedure Encode(const Content: string; EncodeOptions: Integer;
      ecLevel: TErrorCorrectionLevel; QRCode: TQRCode);
  public
    constructor Create;
    property EncoderError: Boolean read FEncoderError;
  end;

function TEncoder.ApplyMaskPenaltyRule1(Matrix: TByteMatrix): Integer;
begin
  Result := ApplyMaskPenaltyRule1Internal(Matrix, True) +
    ApplyMaskPenaltyRule1Internal(Matrix, False);
end;

// Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
// penalty to them.
function TEncoder.ApplyMaskPenaltyRule2(Matrix: TByteMatrix): Integer;
var
  Penalty: Integer;
  TheArray: T2DByteArray;
  Width: Integer;
  Height: Integer;
  X: Integer;
  Y: Integer;
  Value: Integer;
begin
  Penalty := 0;
  TheArray := Matrix.GetArray;
  Width := Matrix.Width;
  Height := Matrix.Height;
  for Y := 0 to Height - 2 do
  begin
    for X := 0 to Width - 2 do
    begin
      Value := TheArray[Y][X];
      if ((Value = TheArray[Y][X + 1]) and (Value = TheArray[Y + 1][X]) and
        (Value = TheArray[Y + 1][X + 1])) then
      begin
        Inc(Penalty, 3);
      end;
    end;
  end;
  Result := Penalty;
end;

// Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or
// 10111010000, and give penalty to them.  If we find patterns like 000010111010000, we give
// penalties twice (i.e. 40 * 2).
function TEncoder.ApplyMaskPenaltyRule3(Matrix: TByteMatrix): Integer;
var
  Penalty: Integer;
  TheArray: T2DByteArray;
  Width: Integer;
  Height: Integer;
  X: Integer;
  Y: Integer;
begin
  Penalty := 0;
  TheArray := Matrix.GetArray;
  Width := Matrix.Width;
  Height := Matrix.Height;
  for Y := 0 to Height - 1 do
  begin
    for X := 0 to Width - 1 do
    begin
      if ((X + 6 < Width) and (TheArray[Y][X] = 1) and (TheArray[Y][X + 1] = 0)
        and (TheArray[Y][X + 2] = 1) and (TheArray[Y][X + 3] = 1) and
        (TheArray[Y][X + 4] = 1) and (TheArray[Y][X + 5] = 0) and
        (TheArray[Y][X + 6] = 1) and
        (((X + 10 < Width) and (TheArray[Y][X + 7] = 0) and
        (TheArray[Y][X + 8] = 0) and (TheArray[Y][X + 9] = 0) and
        (TheArray[Y][X + 10] = 0)) or ((X - 4 >= 0) and (TheArray[Y][X - 1] = 0)
        and (TheArray[Y][X - 2] = 0) and (TheArray[Y][X - 3] = 0) and
        (TheArray[Y][X - 4] = 0)))) then
      begin
        Inc(Penalty, 40);
      end;
      if ((Y + 6 < Height) and (TheArray[Y][X] = 1) and (TheArray[Y + 1][X] = 0)
        and (TheArray[Y + 2][X] = 1) and (TheArray[Y + 3][X] = 1) and
        (TheArray[Y + 4][X] = 1) and (TheArray[Y + 5][X] = 0) and
        (TheArray[Y + 6][X] = 1) and
        (((Y + 10 < Height) and (TheArray[Y + 7][X] = 0) and
        (TheArray[Y + 8][X] = 0) and (TheArray[Y + 9][X] = 0) and
        (TheArray[Y + 10][X] = 0)) or ((Y - 4 >= 0) and (TheArray[Y - 1][X] = 0)
        and (TheArray[Y - 2][X] = 0) and (TheArray[Y - 3][X] = 0) and
        (TheArray[Y - 4][X] = 0)))) then
      begin
        Inc(Penalty, 40);
      end;
    end;
  end;
  Result := Penalty;
end;

// Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
// penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples:
// -   0% => 100
// -  40% =>  20
// -  45% =>  10
// -  50% =>   0
// -  55% =>  10
// -  55% =>  20
// - 100% => 100
function TEncoder.ApplyMaskPenaltyRule4(Matrix: TByteMatrix): Integer;
var
  NumDarkCells: Integer;
  TheArray: T2DByteArray;
  Width: Integer;
  Height: Integer;
  NumTotalCells: Integer;
  DarkRatio: Double;
  X: Integer;
  Y: Integer;
begin
  NumDarkCells := 0;
  TheArray := Matrix.GetArray;
  Width := Matrix.Width;
  Height := Matrix.Height;
  for Y := 0 to Height - 1 do
  begin
    for X := 0 to Width - 1 do
    begin
      if (TheArray[Y][X] = 1) then
      begin
        Inc(NumDarkCells);
      end;
    end;
  end;
  NumTotalCells := Matrix.Height * Matrix.Width;
  DarkRatio := NumDarkCells / NumTotalCells;
  Result := Round(Abs((DarkRatio * 100 - 50)) / 50);
end;

// Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
// vertical and horizontal orders respectively.
function TEncoder.ApplyMaskPenaltyRule1Internal(Matrix: TByteMatrix;
  IsHorizontal: Boolean): Integer;
var
  Penalty: Integer;
  NumSameBitCells: Integer;
  PrevBit: Integer;
  TheArray: T2DByteArray;
  I: Integer;
  J: Integer;
  Bit: Integer;
  ILimit: Integer;
  JLimit: Integer;
begin
  Penalty := 0;
  NumSameBitCells := 0;
  PrevBit := -1;
  // Horizontal mode:
  // for (int i = 0; i < matrix.height(); ++i) {
  // for (int j = 0; j < matrix.width(); ++j) {
  // int bit = matrix.get(i, j);
  // Vertical mode:
  // for (int i = 0; i < matrix.width(); ++i) {
  // for (int j = 0; j < matrix.height(); ++j) {
  // int bit = matrix.get(j, i);
  if (IsHorizontal) then
  begin
    ILimit := Matrix.Height;
    JLimit := Matrix.Width;
  end
  else
  begin
    ILimit := Matrix.Width;
    JLimit := Matrix.Height;
  end;
  TheArray := Matrix.GetArray;

  for I := 0 to ILimit - 1 do
  begin
    for J := 0 to JLimit - 1 do
    begin
      if (IsHorizontal) then
      begin
        Bit := TheArray[I][J];
      end
      else
      begin
        Bit := TheArray[J][I];
      end;
      if (Bit = PrevBit) then
      begin
        Inc(NumSameBitCells);
        // Found five repetitive cells with the same color (bit).
        // We'll give penalty of 3.
        if (NumSameBitCells = 5) then
        begin
          Inc(Penalty, 3);
        end
        else if (NumSameBitCells > 5) then
        begin
          // After five repetitive cells, we'll add the penalty one
          // by one.
          Inc(Penalty, 1);;
        end;
      end
      else
      begin
        NumSameBitCells := 1; // Include the cell itself.
        PrevBit := Bit;
      end;
    end;
    NumSameBitCells := 0; // Clear at each row/column.
  end;
  Result := Penalty;
end;

{ TQRCode }

constructor TQRCode.Create;
begin
  FMode := qmTerminator;
  FQRCodeError := False;
  FECLevel := nil;
  FVersion := -1;
  FMatrixWidth := -1;
  FMaskPattern := -1;
  FNumTotalBytes := -1;
  FNumDataBytes := -1;
  FNumECBytes := -1;
  FNumRSBlocks := -1;
  FMatrix := nil;
end;

destructor TQRCode.Destroy;
begin
  if (Assigned(FECLevel)) then
  begin
    FECLevel.Free;
  end;
  if (Assigned(FMatrix)) then
  begin
    FMatrix.Free;
  end;
  inherited;
end;

function TQRCode.At(X, Y: Integer): Integer;
var
  Value: Integer;
begin
  // The value must be zero or one.
  Value := FMatrix.Get(X, Y);
  if (not((Value = 0) or (Value = 1))) then
  begin
    FQRCodeError := True;
  end;
  Result := Value;
end;

function TQRCode.IsValid: Boolean;
begin
  Result :=
  // First check if all version are not uninitialized.
    ((FECLevel <> nil) and (FVersion <> -1) and (FMatrixWidth <> -1) and
    (FMaskPattern <> -1) and (FNumTotalBytes <> -1) and (FNumDataBytes <> -1)
    and (FNumECBytes <> -1) and (FNumRSBlocks <> -1) and
    // Then check them in other ways..
    IsValidMaskPattern(FMaskPattern) and (FNumTotalBytes = FNumDataBytes +
    FNumECBytes) and
    // ByteMatrix stuff.
    (Assigned(FMatrix)) and (FMatrixWidth = FMatrix.Width) and
    // See 7.3.1 of JISX0510:2004 (Fp.5).
    (FMatrix.Width = FMatrix.Height)); // Must be square.
end;

function TQRCode.IsValidMaskPattern(MaskPattern: Integer): Boolean;
begin
  Result := (MaskPattern >= 0) and (MaskPattern < NUM_MASK_PATTERNS);
end;

procedure TQRCode.SetMatrix(NewMatrix: TByteMatrix);
begin
  if (Assigned(FMatrix)) then
  begin
    FMatrix.Free;
    FMatrix := nil;
  end;
  FMatrix := NewMatrix;
end;

procedure TQRCode.SetAll(VersionNum, NumBytes, NumDataBytes, NumRSBlocks,
  NumECBytes, MatrixWidth: Integer);
begin
  FVersion := VersionNum;
  FNumTotalBytes := NumBytes;
  FNumDataBytes := NumDataBytes;
  FNumRSBlocks := NumRSBlocks;
  FNumECBytes := NumECBytes;
  FMatrixWidth := MatrixWidth;
end;

procedure TQRCode.SetECLevel(NewECLevel: TErrorCorrectionLevel);
begin
  if (Assigned(FECLevel)) then
  begin
    FECLevel.Free;
  end;
  FECLevel := TErrorCorrectionLevel.Create;
  FECLevel.Assign(NewECLevel);
end;

{ TByteMatrix }

procedure TByteMatrix.Clear(Value: Byte);
var
  X, Y: Integer;
begin
  for Y := 0 to FHeight - 1 do
  begin
    for X := 0 to FWidth - 1 do
    begin
      Bytes[Y][X] := Value;
    end;
  end;
end;

constructor TByteMatrix.Create(Width, Height: Integer);
var
  Y: Integer;
  X: Integer;
begin
  FWidth := Width;
  FHeight := Height;
  SetLength(Bytes, Height);
  for Y := 0 to Height - 1 do
  begin
    SetLength(Bytes[Y], Width);
    for X := 0 to Width - 1 do
    begin
      Bytes[Y][X] := 0;
    end;
  end;
end;

function TByteMatrix.Get(X, Y: Integer): Integer;
begin
  if (Bytes[Y][X] = 255) then
    Result := -1
  else
    Result := Bytes[Y][X];
end;

function TByteMatrix.GetArray: T2DByteArray;
begin
  Result := Bytes;
end;

function TByteMatrix.Hash: string;
var
  X, Y: Integer;
  Counter: Integer;
  CC: Integer;
begin
  Result := '';
  for Y := 0 to FHeight - 1 do
  begin
    Counter := 0;
    for X := 0 to FWidth - 1 do
    begin
      CC := Get(X, Y);
      if (CC = -1) then
        CC := 255;
      Counter := Counter + CC;
    end;
    Result := Result + Char((Counter mod 26) + 65);
  end;
end;

procedure TByteMatrix.SetBoolean(X, Y: Integer; Value: Boolean);
begin
  Bytes[Y][X] := Byte(Value) and $FF;
end;

procedure TByteMatrix.SetInteger(X, Y, Value: Integer);
begin
  Bytes[Y][X] := Value and $FF;
end;

procedure TByteMatrix.Assign(Source: TByteMatrix);
var
  SourceLength: Integer;
begin
  SourceLength := Length(Source.Bytes);
  SetLength(Bytes, SourceLength);
  if (SourceLength > 0) then
  begin
    Move(Source.Bytes[0], Bytes[0], SourceLength);
  end;
  FWidth := Source.Width;
  FHeight := Source.Height;
end;

{ TEncoder }

function TEncoder.CalculateMaskPenalty(Matrix: TByteMatrix): Integer;
var
  Penalty: Integer;
begin
  Penalty := 0;
  Inc(Penalty, ApplyMaskPenaltyRule1(Matrix));
  Inc(Penalty, ApplyMaskPenaltyRule2(Matrix));
  Inc(Penalty, ApplyMaskPenaltyRule3(Matrix));
  Inc(Penalty, ApplyMaskPenaltyRule4(Matrix));
  Result := Penalty;
end;

{ procedure TEncoder.Encode(const Content: string; ECLevel: TErrorCorrectionLevel; QRCode: TQRCode);
  begin
  Encode(Content, ECLevel, nil, QRCode);
  end; }

procedure TEncoder.Encode(const Content: string; EncodeOptions: Integer;
  ecLevel: TErrorCorrectionLevel; QRCode: TQRCode);
var
  Mode: TMode;
  DataBits: TBitArray;
  FinalBits: TBitArray;
  HeaderBits: TBitArray;
  HeaderAndDataBits: TBitArray;
  Matrix: TByteMatrix;
  NumLetters: Integer;
  MatrixUtil: TMatrixUtil;
  BitsNeeded: Integer;
  ProvisionalBitsNeeded: Integer;
  ProvisionalVersion: TVersion;
  Version: TVersion;
  ECBlocks: TECBlocks;
  NumDataBytes: Integer;
  Dimension: Integer;
  FilteredContent: string;
begin
  DataBits := TBitArray.Create;
  HeaderBits := TBitArray.Create;

  // Pick an encoding mode appropriate for the content. Note that this will not attempt to use
  // multiple modes / segments even if that were more efficient. Twould be nice.
  // Collect data within the main segment, separately, to count its size if needed. Don't add it to
  // main payload yet.

  Mode := ChooseMode(Content, EncodeOptions);
  FilteredContent := FilterContent(Content, Mode, EncodeOptions);
  AppendBytes(FilteredContent, Mode, DataBits, EncodeOptions);

  // (With ECI in place,) Write the mode marker
  AppendModeInfo(Mode, HeaderBits);

  // Hard part: need to know version to know how many bits length takes. But need to know how many
  // bits it takes to know version. First we take a guess at version by assuming version will be
  // the minimum, 1:
  ProvisionalVersion := TVersion.GetVersionForNumber(1);
  try
    ProvisionalBitsNeeded := HeaderBits.GetSize + GetModeCharacterCountBits
      (Mode, ProvisionalVersion) + DataBits.GetSize;
  finally
    ProvisionalVersion.Free;
  end;

  ProvisionalVersion := TVersion.ChooseVersion(ProvisionalBitsNeeded, ecLevel);
  try
    // Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
    BitsNeeded := HeaderBits.GetSize + GetModeCharacterCountBits(Mode,
      ProvisionalVersion) + DataBits.GetSize;
    Version := TVersion.ChooseVersion(BitsNeeded, ecLevel);
  finally
    ProvisionalVersion.Free;
  end;

  HeaderAndDataBits := TBitArray.Create;
  FinalBits := TBitArray.Create;
  try
    HeaderAndDataBits.AppendBitArray(HeaderBits);

    // Find "length" of main segment and write it
    if (Mode = qmByte) then
    begin
      NumLetters := DataBits.GetSizeInBytes;
    end
    else
    begin
      NumLetters := Length(FilteredContent);
    end;
    AppendLengthInfo(NumLetters, Version.VersionNumber, Mode,
      HeaderAndDataBits);
    // Put data together into the overall payload
    HeaderAndDataBits.AppendBitArray(DataBits);

    ECBlocks := Version.GetECBlocksForLevel(ecLevel);
    NumDataBytes := Version.GetTotalCodewords - ECBlocks.GetTotalECCodewords;

    // Terminate the bits properly.
    TerminateBits(NumDataBytes, HeaderAndDataBits);

    // Interleave data bits with error correction code.
    InterleaveWithECBytes(HeaderAndDataBits, Version.GetTotalCodewords,
      NumDataBytes, ECBlocks.GetNumBlocks, FinalBits);

    // QRCode qrCode = new QRCode();  // This is passed in

    QRCode.SetECLevel(ecLevel);
    QRCode.Mode := Mode;
    QRCode.Version := Version.VersionNumber;

    // Choose the mask pattern and set to "qrCode".
    Dimension := Version.GetDimensionForVersion;
    Matrix := TByteMatrix.Create(Dimension, Dimension);

    QRCode.MaskPattern := ChooseMaskPattern(FinalBits, ecLevel,
      Version.VersionNumber, Matrix);

    Matrix.Free;
    Matrix := TByteMatrix.Create(Dimension, Dimension);

    // Build the matrix and set it to "qrCode".
    MatrixUtil := TMatrixUtil.Create;
    try
      MatrixUtil.BuildMatrix(FinalBits, QRCode.ecLevel, QRCode.Version,
        QRCode.MaskPattern, Matrix);
    finally
      MatrixUtil.Free;
    end;

    QRCode.SetMatrix(Matrix); // QRCode will free the matrix
  finally
    DataBits.Free;
    HeaderAndDataBits.Free;
    FinalBits.Free;
    HeaderBits.Free;
    Version.Free;
  end;
end;

function TEncoder.FilterContent(const Content: string; Mode: TMode;
  EncodeOptions: Integer): string;
var
  X: Integer;
  CanAdd: Boolean;
begin
  Result := '';
  // for X := 1 to Length(Content) do
  for X := Low(Content) to High(Content) do // 2015-02-04,edited by vclclx。
  begin
    CanAdd := False;
    if (Mode = qmNumeric) then
    begin
      CanAdd := (Content[X] >= '0') and (Content[X] <= '9');
    end
    else if (Mode = qmAlphanumeric) then
    begin
      CanAdd := GetAlphanumericCode(Ord(Content[X])) > 0;
    end
    else if (Mode = qmByte) then
    begin
      if (EncodeOptions = 3) then
      begin
        CanAdd := Ord(Content[X]) <= $FF;
      end
      else if ((EncodeOptions = 4) or (EncodeOptions = 5)) then
      begin
        CanAdd := True;
      end;
    end;
    if (CanAdd) then
    begin
      Result := Result + Content[X];
    end;
  end;
end;

// Return the code point of the table used in alphanumeric mode or
// -1 if there is no corresponding code in the table.
function TEncoder.GetAlphanumericCode(Code: Integer): Integer;
begin
  if (Code < Length(ALPHANUMERIC_TABLE)) then
  begin
    Result := ALPHANUMERIC_TABLE[Code];
  end
  else
  begin
    Result := -1;
  end;
end;

// Choose the mode based on the content
function TEncoder.ChooseMode(const Content: string;
  var EncodeOptions: Integer): TMode;
var
  AllNumeric: Boolean;
  AllAlphanumeric: Boolean;
  AllISO: Boolean;
  I: Integer;
  C: WideChar;
begin
  if (EncodeOptions = 0) then
  begin
    AllNumeric := Length(Content) > 0;
    // I := 1;
    // while (I <= Length(Content)) and (AllNumeric) do
    I := Low(Content); // 2015-02-04,edited by vclclx。
    while (I <= High(Content)) and (AllNumeric) do
    // 2015-02-04,edited by vclclx。
    begin
      C := Content[I];
      if ((C < '0') or (C > '9')) then
      begin
        AllNumeric := False;
      end
      else
      begin
        Inc(I);
      end;
    end;

    if (not AllNumeric) then
    begin
      AllAlphanumeric := Length(Content) > 0;
      // I := 1;
      // while (I <= Length(Content)) and (AllAlphanumeric) do
      I := Low(Content); // 2015-02-04,edited by vclclx。
      while (I <= High(Content)) and (AllAlphanumeric) do
      // 2015-02-04,edited by vclclx。
      begin
        C := Content[I];
        if (GetAlphanumericCode(Ord(C)) < 0) then
        begin
          AllAlphanumeric := False;
        end
        else
        begin
          Inc(I);
        end;
      end;
    end
    else
    begin
      AllAlphanumeric := False;
    end;

    if (not AllAlphanumeric) then
    begin
      AllISO := Length(Content) > 0;
      // I := 1;
      // while (I <= Length(Content)) and (AllISO) do
      I := Low(Content); // 2015-02-04,edited by vclclx。
      while (I <= High(Content)) and (AllISO) do // 2015-02-04,edited by vclclx。
      begin
        C := Content[I];
        if (Ord(C) > $FF) then
        begin
          AllISO := False;
        end
        else
        begin
          Inc(I);
        end;
      end;
    end
    else
    begin
      AllISO := False;
    end;

    if (AllNumeric) then
    begin
      Result := qmNumeric;
    end
    else if (AllAlphanumeric) then
    begin
      Result := qmAlphanumeric;
    end
    else if (AllISO) then
    begin
      Result := qmByte;
      EncodeOptions := 3;
    end
    else
    begin
      Result := qmByte;
      EncodeOptions := 4;
    end;
  end
  else if (EncodeOptions = 1) then
  begin
    Result := qmNumeric;
  end
  else if (EncodeOptions = 2) then
  begin
    Result := qmAlphanumeric;
  end
  else
  begin
    Result := qmByte;
  end;
end;

constructor TEncoder.Create;
begin
  FEncoderError := False;
end;

{ function TEncoder.IsOnlyDoubleByteKanji(const Content: string): Boolean;
  var
  I: Integer;
  Char1: Integer;
  begin
  Result := True;
  I := 0;
  while ((I < Length(Content)) and Result) do
  begin
  Char1 := Ord(Content[I + 1]);
  if (((Char1 < $81) or (Char1 > $9F)) and ((Char1 < $E0) or (Char1 > $EB))) then
  begin
  Result := False;
  end;
  end;
  end; }

function TEncoder.ChooseMaskPattern(Bits: TBitArray;
  ecLevel: TErrorCorrectionLevel; Version: Integer;
  Matrix: TByteMatrix): Integer;
var
  MinPenalty: Integer;
  BestMaskPattern: Integer;
  MaskPattern: Integer;
  MatrixUtil: TMatrixUtil;
  Penalty: Integer;
begin
  MinPenalty := MaxInt;
  BestMaskPattern := -1;

  // We try all mask patterns to choose the best one.
  for MaskPattern := 0 to NUM_MASK_PATTERNS - 1 do
  begin
    MatrixUtil := TMatrixUtil.Create;
    try
      MatrixUtil.BuildMatrix(Bits, ecLevel, Version, MaskPattern, Matrix);
    finally
      MatrixUtil.Free;
    end;
    Penalty := CalculateMaskPenalty(Matrix);
    if (Penalty < MinPenalty) then
    begin
      MinPenalty := Penalty;
      BestMaskPattern := MaskPattern;
    end;
  end;

  Result := BestMaskPattern;
end;

// Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
procedure TEncoder.TerminateBits(NumDataBytes: Integer; var Bits: TBitArray);
var
  Capacity: Integer;
  I: Integer;
  NumBitsInLastByte: Integer;
  NumPaddingBytes: Integer;
begin
  Capacity := NumDataBytes shl 3;
  if (Bits.GetSize > Capacity) then
  begin
    FEncoderError := True;
    Exit;
  end;
  I := 0;
  while ((I < 4) and (Bits.GetSize < Capacity)) do
  begin
    Bits.AppendBit(False);
    Inc(I);
  end;

  // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
  // If the last byte isn't 8-bit aligned, we'll add padding bits.
  NumBitsInLastByte := Bits.GetSize and $07;
  if (NumBitsInLastByte > 0) then
  begin
    for I := NumBitsInLastByte to 7 do
    begin
      Bits.AppendBit(False);
    end;
  end;

  // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
  NumPaddingBytes := NumDataBytes - Bits.GetSizeInBytes;
  for I := 0 to NumPaddingBytes - 1 do
  begin
    if ((I and $01) = 0) then
    begin
      Bits.AppendBits($EC, 8);
    end
    else
    begin
      Bits.AppendBits($11, 8);
    end;
  end;
  if (Bits.GetSize <> Capacity) then
  begin
    FEncoderError := True;
  end;
end;

// Get number of data bytes and number of error correction bytes for block id "blockID". Store
// the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
// JISX0510:2004 (p.30)
procedure TEncoder.GetNumDataBytesAndNumECBytesForBlockID(NumTotalBytes,
  NumDataBytes, NumRSBlocks, BlockID: Integer;
  var NumDataBytesInBlock: TIntegerArray; var NumECBytesInBlock: TIntegerArray);
var
  NumRSBlocksInGroup1: Integer;
  NumRSBlocksInGroup2: Integer;
  NumTotalBytesInGroup1: Integer;
  NumTotalBytesInGroup2: Integer;
  NumDataBytesInGroup1: Integer;
  NumDataBytesInGroup2: Integer;
  NumECBytesInGroup1: Integer;
  NumECBytesInGroup2: Integer;
begin
  if (BlockID >= NumRSBlocks) then
  begin
    FEncoderError := True;
    Exit;
  end;
  // numRsBlocksInGroup2 = 196 % 5 = 1
  NumRSBlocksInGroup2 := NumTotalBytes mod NumRSBlocks;
  // numRsBlocksInGroup1 = 5 - 1 = 4
  NumRSBlocksInGroup1 := NumRSBlocks - NumRSBlocksInGroup2;
  // numTotalBytesInGroup1 = 196 / 5 = 39
  NumTotalBytesInGroup1 := NumTotalBytes div NumRSBlocks;
  // numTotalBytesInGroup2 = 39 + 1 = 40
  NumTotalBytesInGroup2 := NumTotalBytesInGroup1 + 1;
  // numDataBytesInGroup1 = 66 / 5 = 13
  NumDataBytesInGroup1 := NumDataBytes div NumRSBlocks;
  // numDataBytesInGroup2 = 13 + 1 = 14
  NumDataBytesInGroup2 := NumDataBytesInGroup1 + 1;
  // numEcBytesInGroup1 = 39 - 13 = 26
  NumECBytesInGroup1 := NumTotalBytesInGroup1 - NumDataBytesInGroup1;
  // numEcBytesInGroup2 = 40 - 14 = 26
  NumECBytesInGroup2 := NumTotalBytesInGroup2 - NumDataBytesInGroup2;
  // Sanity checks.
  // 26 = 26
  if (NumECBytesInGroup1 <> NumECBytesInGroup2) then
  begin
    FEncoderError := True;
    Exit;
  end;
  // 5 = 4 + 1.
  if (NumRSBlocks <> (NumRSBlocksInGroup1 + NumRSBlocksInGroup2)) then
  begin
    FEncoderError := True;
    Exit;
  end;
  // 196 = (13 + 26) * 4 + (14 + 26) * 1
  if (NumTotalBytes <> ((NumDataBytesInGroup1 + NumECBytesInGroup1) *
    NumRSBlocksInGroup1) + ((NumDataBytesInGroup2 + NumECBytesInGroup2) *
    NumRSBlocksInGroup2)) then
  begin
    FEncoderError := True;
    Exit;
  end;

  if (BlockID < NumRSBlocksInGroup1) then
  begin
    NumDataBytesInBlock[0] := NumDataBytesInGroup1;
    NumECBytesInBlock[0] := NumECBytesInGroup1;
  end
  else
  begin
    NumDataBytesInBlock[0] := NumDataBytesInGroup2;
    NumECBytesInBlock[0] := NumECBytesInGroup2;
  end;
end;

// Interleave "bits" with corresponding error correction bytes. On success, store the result in
// "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
procedure TEncoder.InterleaveWithECBytes(Bits: TBitArray;
  NumTotalBytes, NumDataBytes, NumRSBlocks: Integer; var Result: TBitArray);
var
  DataBytesOffset: Integer;
  MaxNumDataBytes: Integer;
  MaxNumECBytes: Integer;
  Blocks: TObjectList<TBlockPair>;
  NumDataBytesInBlock: TIntegerArray;
  NumECBytesInBlock: TIntegerArray;
  Size: Integer;
  DataBytes: TByteArray;
  ECBytes: TByteArray;
  I, J: Integer;
  BlockPair: TBlockPair;
begin
  SetLength(ECBytes, 0);

  // "bits" must have "getNumDataBytes" bytes of data.
  if (Bits.GetSizeInBytes <> NumDataBytes) then
  begin
    FEncoderError := True;
    Exit;
  end;

  // Step 1.  Divide data bytes into blocks and generate error correction bytes for them. We'll
  // store the divided data bytes blocks and error correction bytes blocks into "blocks".
  DataBytesOffset := 0;
  MaxNumDataBytes := 0;
  MaxNumECBytes := 0;

  // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
  Blocks := TObjectList<TBlockPair>.Create(True);
  try
    Blocks.Capacity := NumRSBlocks;

    for I := 0 to NumRSBlocks - 1 do
    begin
      SetLength(NumDataBytesInBlock, 1);
      SetLength(NumECBytesInBlock, 1);
      GetNumDataBytesAndNumECBytesForBlockID(NumTotalBytes, NumDataBytes,
        NumRSBlocks, I, NumDataBytesInBlock, NumECBytesInBlock);

      Size := NumDataBytesInBlock[0];
      SetLength(DataBytes, Size);
      Bits.ToBytes(8 * DataBytesOffset, DataBytes, 0, Size);
      ECBytes := GenerateECBytes(DataBytes, NumECBytesInBlock[0]);
      BlockPair := TBlockPair.Create(DataBytes, ECBytes);
      Blocks.Add(BlockPair);

      MaxNumDataBytes := Max(MaxNumDataBytes, Size);
      MaxNumECBytes := Max(MaxNumECBytes, Length(ECBytes));
      Inc(DataBytesOffset, NumDataBytesInBlock[0]);
    end;
    if (NumDataBytes <> DataBytesOffset) then
    begin
      FEncoderError := True;
      Exit;
    end;

    // First, place data blocks.
    for I := 0 to MaxNumDataBytes - 1 do
    begin
      for J := 0 to Blocks.Count - 1 do
      begin
        DataBytes := TBlockPair(Blocks.Items[J]).GetDataBytes;
        if (I < Length(DataBytes)) then
        begin
          Result.AppendBits(DataBytes[I], 8);
        end;
      end;
    end;
    // Then, place error correction blocks.
    for I := 0 to MaxNumECBytes - 1 do
    begin
      for J := 0 to Blocks.Count - 1 do
      begin
        ECBytes := TBlockPair(Blocks.Items[J]).GetErrorCorrectionBytes;
        if (I < Length(ECBytes)) then
        begin
          Result.AppendBits(ECBytes[I], 8);
        end;
      end;
    end;
  finally
    Blocks.Free;
  end;
  if (NumTotalBytes <> Result.GetSizeInBytes) then // Should be same.
  begin
    FEncoderError := True;
    Exit;
  end;
end;

function TEncoder.GenerateECBytes(DataBytes: TByteArray;
  NumECBytesInBlock: Integer): TByteArray;
var
  NumDataBytes: Integer;
  ToEncode: TIntegerArray;
  ReedSolomonEncoder: TReedSolomonEncoder;
  I: Integer;
  ECBytes: TByteArray;
  GenericGF: TGenericGF;
begin
  NumDataBytes := Length(DataBytes);
  SetLength(ToEncode, NumDataBytes + NumECBytesInBlock);

  for I := 0 to NumDataBytes - 1 do
  begin
    ToEncode[I] := DataBytes[I] and $FF;
  end;

  GenericGF := TGenericGF.CreateQRCodeField256;
  try
    ReedSolomonEncoder := TReedSolomonEncoder.Create(GenericGF);
    try
      ReedSolomonEncoder.Encode(ToEncode, NumECBytesInBlock);
    finally
      ReedSolomonEncoder.Free;
    end;
  finally
    GenericGF.Free;
  end;

  SetLength(ECBytes, NumECBytesInBlock);
  for I := 0 to NumECBytesInBlock - 1 do
  begin
    ECBytes[I] := ToEncode[NumDataBytes + I];
  end;

  Result := ECBytes;
end;

// Append mode info. On success, store the result in "bits".
procedure TEncoder.AppendModeInfo(Mode: TMode; Bits: TBitArray);
begin
  Bits.AppendBits(GetModeBits(Mode), 4);
end;

// Append length info. On success, store the result in "bits".
procedure TEncoder.AppendLengthInfo(NumLetters, VersionNum: Integer;
  Mode: TMode; Bits: TBitArray);
var
  NumBits: Integer;
  Version: TVersion;
begin
  Version := TVersion.GetVersionForNumber(VersionNum);
  try
    NumBits := GetModeCharacterCountBits(Mode, Version);
  finally
    Version.Free;
  end;

  if (NumLetters > ((1 shl NumBits) - 1)) then
  begin
    FEncoderError := True;
    Exit;
  end;

  Bits.AppendBits(NumLetters, NumBits);
end;

// Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
procedure TEncoder.AppendBytes(const Content: string; Mode: TMode;
  Bits: TBitArray; EncodeOptions: Integer);
begin
  if (Mode = qmNumeric) then
  begin
    AppendNumericBytes(Content, Bits);
  end
  else if (Mode = qmAlphanumeric) then
  begin
    AppendAlphanumericBytes(Content, Bits);
  end
  else if (Mode = qmByte) then
  begin
    Append8BitBytes(Content, Bits, EncodeOptions);
  end
  else if (Mode = qmKanji) then
  begin
    AppendKanjiBytes(Content, Bits);
  end
  else
  begin
    FEncoderError := True;
    Exit;
  end;
end;

procedure TEncoder.AppendNumericBytes(const Content: string; Bits: TBitArray);
var
  ContentLength: Integer;
  I: Integer;
  Num1: Integer;
  Num2: Integer;
  Num3: Integer;
begin
  ContentLength := Length(Content);
  // I := 0;
  // while (I < ContentLength) do
  I := Low(Content); // 2015-02-04,edited by vclclx。
  while (I <= High(Content)) do // 2015-02-04,edited by vclclx。
  begin
    // Num1 := Ord(Content[I + 0 + 1]) - Ord('0');
    Num1 := Ord(Content[I + 0]) - Ord('0'); // 2015-02-04,edited by vclclx。
    // if (I + 2 < ContentLength) then
    if (I + 2 <= High(Content)) then // 2015-02-04,edited by vclclx。
    begin
      // Encode three numeric letters in ten bits.
      // Num2 := Ord(Content[I + 1 + 1]) - Ord('0');
      // Num3 := Ord(Content[I + 2 + 1]) - Ord('0');
      Num2 := Ord(Content[I + 1]) - Ord('0'); // 2015-02-04,edited by vclclx。
      Num3 := Ord(Content[I + 2]) - Ord('0'); // 2015-02-04,edited by vclclx。
      Bits.AppendBits(Num1 * 100 + Num2 * 10 + Num3, 10);
      Inc(I, 3);
    end
    else
      // if (I + 1 < ContentLength) then
      if (I + 1 <= High(Content)) then // 2015-02-04,edited by vclclx。
      begin
        // Encode two numeric letters in seven bits.
        // Num2 := Ord(Content[I + 1 + 1]) - Ord('0');
        Num2 := Ord(Content[I + 1]) - Ord('0'); // 2015-02-04,edited by vclclx。
        Bits.AppendBits(Num1 * 10 + Num2, 7);
        Inc(I, 2);
      end
      else
      begin
        // Encode one numeric letter in four bits.
        Bits.AppendBits(Num1, 4);
        Inc(I);
      end;
  end;
end;

procedure TEncoder.AppendAlphanumericBytes(const Content: string;
  Bits: TBitArray);
var
  ContentLength: Integer;
  I: Integer;
  Code1: Integer;
  Code2: Integer;
begin
  ContentLength := Length(Content);
  // I := 0;
  // while (I < ContentLength) do
  I := Low(Content); // 2015-02-04,edited by vclclx。
  while (I <= High(Content)) do // 2015-02-04,edited by vclclx。
  begin
    // Code1 := GetAlphanumericCode(Ord(Content[I + 0 + 1]));
    Code1 := GetAlphanumericCode(Ord(Content[I + 0]));
    // 2015-02-04,edited by vclclx。
    if (Code1 = -1) then
    begin
      FEncoderError := True;
      Exit;
    end;
    // if (I + 1 < ContentLength) then
    if (I + 1 <= High(Content)) then // 2015-02-04,edited by vclclx。
    begin
      // Code2 := GetAlphanumericCode(Ord(Content[I + 1 + 1]));
      Code2 := GetAlphanumericCode(Ord(Content[I + 1]));
      // 2015-02-04,edited by vclclx。
      if (Code2 = -1) then
      begin
        FEncoderError := True;
        Exit;
      end;
      // Encode two alphanumeric letters in 11 bits.
      Bits.AppendBits(Code1 * 45 + Code2, 11);
      Inc(I, 2);
    end
    else
    begin
      // Encode one alphanumeric letter in six bits.
      Bits.AppendBits(Code1, 6);
      Inc(I);
    end;
  end;
end;

procedure TEncoder.Append8BitBytes(const Content: string; Bits: TBitArray;
  EncodeOptions: Integer);
var
  Bytes: TByteArray;
  I: Integer;
  // UTF8Version: string;
  UTF8Bytes: TBytes; // 2015-02-04,edited by vclclx。
begin
  SetLength(Bytes, 0);
  if (EncodeOptions = 3) then
  begin
    SetLength(Bytes, Length(Content));
    // for I := 1 to Length(Content) do
    for I := Low(Content) to High(Content) do // 2015-02-04,edited by vclclx。
    begin
      // Bytes[I - 1] := Ord(Content[I]) and $FF;
      Bytes[I] := Ord(Content[I]) and $FF; // 2015-02-04,edited by vclclx。
    end;
  end
  else if (EncodeOptions = 4) then
  begin
    // Add the UTF-8 BOM
    // UTF8Version := #$EF#$BB#$BF + UTF8Encode(Content);
    // SetLength(Bytes, Length(UTF8Version));
    // if (Length(UTF8Version) > 0) then
    // begin
    // Move(UTF8Version[1], Bytes[0], Length(UTF8Version));
    // end;

    // 2015-02-04,edited by vclclx。
    Bytes := [$EF, $BB, $BF];
    with TUTF8Encoding.Create do
      try
        UTF8Bytes := GetBytes(Content);
      finally
        Free;
      end;
    if Length(UTF8Bytes) > 0 then
    begin
      SetLength(Bytes, 3 + Length(UTF8Bytes));
      Move(UTF8Bytes[0], Bytes[3], Length(UTF8Bytes));
    end;
  end
  else if (EncodeOptions = 5) then
  begin
    // No BOM
    // UTF8Version := UTF8Encode(Content);
    // SetLength(Bytes, Length(UTF8Version));
    // if (Length(UTF8Version) > 0) then
    // begin
    // Move(UTF8Version[1], Bytes[0], Length(UTF8Version));
    // end;

    // 2015-02-04,edited by vclclx。
    with TUTF8Encoding.Create do
      try
        UTF8Bytes := GetBytes(Content);
      finally
        Free;
      end;
    if Length(UTF8Bytes) > 0 then
    begin
      SetLength(Bytes, Length(UTF8Bytes));
      Move(UTF8Bytes[0], Bytes[0], Length(UTF8Bytes));
    end;
  end;
  for I := 0 to Length(Bytes) - 1 do
  begin
    Bits.AppendBits(Bytes[I], 8);
  end;
end;

procedure TEncoder.AppendKanjiBytes(const Content: string; Bits: TBitArray);
var
  Bytes: TByteArray;
  ByteLength: Integer;
  I: Integer;
  Byte1: Integer;
  Byte2: Integer;
  Code: Integer;
  Subtracted: Integer;
  Encoded: Integer;
begin
  SetLength(Bytes, 0);
  try

  except
    FEncoderError := True;
    Exit;
  end;

  ByteLength := Length(Bytes);
  I := 0;
  while (I < ByteLength) do
  begin
    Byte1 := Bytes[I] and $FF;
    Byte2 := Bytes[I + 1] and $FF;
    Code := (Byte1 shl 8) or Byte2;
    Subtracted := -1;
    if ((Code >= $8140) and (Code <= $9FFC)) then
    begin
      Subtracted := Code - $8140;
    end
    else if ((Code >= $E040) and (Code <= $EBBF)) then
    begin
      Subtracted := Code - $C140;
    end;
    if (Subtracted = -1) then
    begin
      FEncoderError := True;
      Exit;
    end;
    Encoded := ((Subtracted shr 8) * $C0) + (Subtracted and $FF);
    Bits.AppendBits(Encoded, 13);
    Inc(I, 2);
  end;
end;

procedure TMatrixUtil.ClearMatrix(Matrix: TByteMatrix);
begin
  Matrix.Clear(Byte(-1));
end;

constructor TMatrixUtil.Create;
begin
  FMatrixUtilError := False;
end;

// Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On
// success, store the result in "matrix" and return true.
procedure TMatrixUtil.BuildMatrix(DataBits: TBitArray;
  ecLevel: TErrorCorrectionLevel; Version, MaskPattern: Integer;
  Matrix: TByteMatrix);
begin
  ClearMatrix(Matrix);
  EmbedBasicPatterns(Version, Matrix);

  // Type information appear with any version.
  EmbedTypeInfo(ecLevel, MaskPattern, Matrix);

  // Version info appear if version >= 7.
  MaybeEmbedVersionInfo(Version, Matrix);

  // Data should be embedded at end.
  EmbedDataBits(DataBits, MaskPattern, Matrix);
end;

// Embed basic patterns. On success, modify the matrix and return true.
// The basic patterns are:
// - Position detection patterns
// - Timing patterns
// - Dark dot at the left bottom corner
// - Position adjustment patterns, if need be
procedure TMatrixUtil.EmbedBasicPatterns(Version: Integer; Matrix: TByteMatrix);
begin
  // Let's get started with embedding big squares at corners.
  EmbedPositionDetectionPatternsAndSeparators(Matrix);

  // Then, embed the dark dot at the left bottom corner.
  EmbedDarkDotAtLeftBottomCorner(Matrix);

  // Position adjustment patterns appear if version >= 2.
  MaybeEmbedPositionAdjustmentPatterns(Version, Matrix);

  // Timing patterns should be embedded after position adj. patterns.
  EmbedTimingPatterns(Matrix);
end;

// Embed type information. On success, modify the matrix.
procedure TMatrixUtil.EmbedTypeInfo(ecLevel: TErrorCorrectionLevel;
  MaskPattern: Integer; Matrix: TByteMatrix);
var
  TypeInfoBits: TBitArray;
  I: Integer;
  Bit: Boolean;
  X1, Y1: Integer;
  X2, Y2: Integer;
begin
  TypeInfoBits := TBitArray.Create;
  try
    MakeTypeInfoBits(ecLevel, MaskPattern, TypeInfoBits);

    for I := 0 to TypeInfoBits.GetSize - 1 do
    begin
      // Place bits in LSB to MSB order.  LSB (least significant bit) is the last value in
      // "typeInfoBits".
      Bit := TypeInfoBits.Get(TypeInfoBits.GetSize - 1 - I);

      // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).
      X1 := TYPE_INFO_COORDINATES[I][0];
      Y1 := TYPE_INFO_COORDINATES[I][1];
      Matrix.SetBoolean(X1, Y1, Bit);

      if (I < 8) then
      begin
        // Right top corner.
        X2 := Matrix.Width - I - 1;
        Y2 := 8;
        Matrix.SetBoolean(X2, Y2, Bit);
      end
      else
      begin
        // Left bottom corner.
        X2 := 8;
        Y2 := Matrix.Height - 7 + (I - 8);
        Matrix.SetBoolean(X2, Y2, Bit);
      end;
    end;
  finally
    TypeInfoBits.Free;
  end;
end;

// Embed version information if need be. On success, modify the matrix and return true.
// See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
procedure TMatrixUtil.MaybeEmbedVersionInfo(Version: Integer;
  Matrix: TByteMatrix);
var
  VersionInfoBits: TBitArray;
  I, J: Integer;
  BitIndex: Integer;
  Bit: Boolean;
begin
  if (Version < 7) then
  begin
    Exit; // Don't need version info.
  end;

  VersionInfoBits := TBitArray.Create;
  try
    MakeVersionInfoBits(Version, VersionInfoBits);

    BitIndex := 6 * 3 - 1; // It will decrease from 17 to 0.
    for I := 0 to 5 do
    begin
      for J := 0 to 2 do
      begin
        // Place bits in LSB (least significant bit) to MSB order.
        Bit := VersionInfoBits.Get(BitIndex);
        Dec(BitIndex);
        // Left bo