zoukankan      html  css  js  c++  java
  • 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

    unit Unit3;
    
    interface
    
    uses
      System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
      FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects,
      FMX.Controls.Presentation, FMX.Edit, FMX.StdCtrls,DelphiZXingQRCode,
      FMX.ListBox,system.math;
    
    type
      TForm3 = class(TForm)
        Button1: TButton;
        edtText: TEdit;
        imgQRCode: TImage;
        cmbEncoding: TComboBox;
        edtQuietZone: TEdit;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
        BMP: TBitmap;
      public
        { Public declarations }
      end;
    
    var
      Form3: TForm3;
    
    implementation
    
    {$R *.fmx}
    
    procedure TForm3.Button1Click(Sender: TObject);
    var
      QRCode: TDelphiZXingQRCode;
    begin
      QRCode := TDelphiZXingQRCode.Create;
      try
        QRCode.Data := edtText.Text;
        QRCode.Encoding := TQRCodeEncoding(cmbEncoding.ItemIndex);
        QRCode.QuietZone := StrToIntDef(edtQuietZone.Text, 4);
        QRCode.DrawQrcode(imgQRCode,QRCode);
      finally
        QRCode.Free;
      end;
    end;
    
    end.
    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 bottom corner.
            Matrix.SetBoolean(I, Matrix.Height - 11 + J, Bit);
            // Right bottom corner.
            Matrix.SetBoolean(Matrix.Height - 11 + J, I, Bit);
          end;
        end;
      finally
        VersionInfoBits.Free;
      end;
    end;
    
    // Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true.
    // For debugging purposes, it skips masking process if "getMaskPattern" is -1.
    // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
    procedure TMatrixUtil.EmbedDataBits(DataBits: TBitArray; MaskPattern: Integer;
      Matrix: TByteMatrix);
    var
      BitIndex: Integer;
      Direction: Integer;
      X, Y, I, XX: Integer;
      Bit: Boolean;
      MaskUtil: TMaskUtil;
    begin
      MaskUtil := TMaskUtil.Create;
      try
        BitIndex := 0;
        Direction := -1;
        // Start from the right bottom cell.
        X := Matrix.Width - 1;
        Y := Matrix.Height - 1;
        while (X > 0) do
        begin
          // Skip the vertical timing pattern.
          if (X = 6) then
          begin
            Dec(X, 1);
          end;
          while ((Y >= 0) and (Y < Matrix.Height)) do
          begin
            for I := 0 to 1 do
            begin
              XX := X - I;
              // Skip the cell if it's not empty.
              if (not IsEmpty(Matrix.Get(XX, Y))) then
              begin
                Continue;
              end;
    
              if (BitIndex < DataBits.GetSize) then
              begin
                Bit := DataBits.Get(BitIndex);
                Inc(BitIndex);
              end
              else
              begin
                // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described
                // in 8.4.9 of JISX0510:2004 (p. 24).
                Bit := False;
              end;
    
              // Skip masking if mask_pattern is -1.
              if (MaskPattern <> -1) then
              begin
                if (MaskUtil.GetDataMaskBit(MaskPattern, XX, Y)) then
                begin
                  Bit := not Bit;
                end;
              end;
              Matrix.SetBoolean(XX, Y, Bit);
            end;
            Inc(Y, Direction);
          end;
          Direction := -Direction; // Reverse the direction.
          Inc(Y, Direction);
          Dec(X, 2); // Move to the left.
        end;
      finally
        MaskUtil.Free;
      end;
    
      // All bits should be consumed.
      if (BitIndex <> DataBits.GetSize()) then
      begin
        FMatrixUtilError := True;
        Exit;
      end;
    end;
    
    // Return the position of the most significant bit set (to one) in the "value". The most
    // significant bit is position 32. If there is no bit set, return 0. Examples:
    // - findMSBSet(0) => 0
    // - findMSBSet(1) => 1
    // - findMSBSet(255) => 8
    function TMatrixUtil.FindMSBSet(Value: Integer): Integer;
    var
      NumDigits: Integer;
    begin
      NumDigits := 0;
      while (Value <> 0) do
      begin
        Value := Value shr 1;
        Inc(NumDigits);
      end;
      Result := NumDigits;
    end;
    
    // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH
    // code is used for encoding type information and version information.
    // Example: Calculation of version information of 7.
    // f(x) is created from 7.
    // - 7 = 000111 in 6 bits
    // - f(x) = x^2 + x^1 + x^0
    // g(x) is given by the standard (p. 67)
    // - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1
    // Multiply f(x) by x^(18 - 6)
    // - f'(x) = f(x) * x^(18 - 6)
    // - f'(x) = x^14 + x^13 + x^12
    // Calculate the remainder of f'(x) / g(x)
    // x^2
    // __________________________________________________
    // g(x) )x^14 + x^13 + x^12
    // x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2
    // --------------------------------------------------
    // x^11 + x^10 + x^7 + x^4 + x^2
    //
    // The remainder is x^11 + x^10 + x^7 + x^4 + x^2
    // Encode it in binary: 110010010100
    // The return value is 0xc94 (1100 1001 0100)
    //
    // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit
    // operations. We don't care if cofficients are positive or negative.
    function TMatrixUtil.CalculateBCHCode(Value, Poly: Integer): Integer;
    var
      MSBSetInPoly: Integer;
    begin
      // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1
      // from 13 to make it 12.
      MSBSetInPoly := FindMSBSet(Poly);
      Value := Value shl (MSBSetInPoly - 1);
      // Do the division business using exclusive-or operations.
      while (FindMSBSet(Value) >= MSBSetInPoly) do
      begin
        Value := Value xor (Poly shl (FindMSBSet(Value) - MSBSetInPoly));
      end;
      // Now the "value" is the remainder (i.e. the BCH code)
      Result := Value;
    end;
    
    // Make bit vector of type information. On success, store the result in "bits" and return true.
    // Encode error correction level and mask pattern. See 8.9 of
    // JISX0510:2004 (p.45) for details.
    procedure TMatrixUtil.MakeTypeInfoBits(ecLevel: TErrorCorrectionLevel;
      MaskPattern: Integer; Bits: TBitArray);
    var
      TypeInfo: Integer;
      BCHCode: Integer;
      MaskBits: TBitArray;
    begin
      if ((MaskPattern >= 0) and (MaskPattern < NUM_MASK_PATTERNS)) then
      begin
        TypeInfo := (ecLevel.Bits shl 3) or MaskPattern;
        Bits.AppendBits(TypeInfo, 5);
    
        BCHCode := CalculateBCHCode(TypeInfo, TYPE_INFO_POLY);
        Bits.AppendBits(BCHCode, 10);
    
        MaskBits := TBitArray.Create;
        try
          MaskBits.AppendBits(TYPE_INFO_MASK_PATTERN, 15);
          Bits.XorOperation(MaskBits);
        finally
          MaskBits.Free;
        end;
    
        if (Bits.GetSize <> 15) then // Just in case.
        begin
          FMatrixUtilError := True;
          Exit;
        end;
      end;
    end;
    
    // Make bit vector of version information. On success, store the result in "bits" and return true.
    // See 8.10 of JISX0510:2004 (p.45) for details.
    procedure TMatrixUtil.MakeVersionInfoBits(Version: Integer; Bits: TBitArray);
    var
      BCHCode: Integer;
    begin
      Bits.AppendBits(Version, 6);
      BCHCode := CalculateBCHCode(Version, VERSION_INFO_POLY);
      Bits.AppendBits(BCHCode, 12);
    
      if (Bits.GetSize() <> 18) then
      begin
        FMatrixUtilError := True;
        Exit;
      end;
    end;
    
    // Check if "value" is empty.
    function TMatrixUtil.IsEmpty(Value: Integer): Boolean;
    begin
      Result := (Value = -1);
    end;
    
    procedure TMatrixUtil.EmbedTimingPatterns(Matrix: TByteMatrix);
    var
      I: Integer;
      Bit: Integer;
    begin
      // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
      // separation patterns (size 1). Thus, 8 = 7 + 1.
      for I := 8 to Matrix.Width - 9 do
      begin
        Bit := (I + 1) mod 2;
        // Horizontal line.
        if (IsEmpty(Matrix.Get(I, 6))) then
        begin
          Matrix.SetInteger(I, 6, Bit);
        end;
        // Vertical line.
        if (IsEmpty(Matrix.Get(6, I))) then
        begin
          Matrix.SetInteger(6, I, Bit);
        end;
      end;
    end;
    
    // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
    procedure TMatrixUtil.EmbedDarkDotAtLeftBottomCorner(Matrix: TByteMatrix);
    begin
      if (Matrix.Get(8, Matrix.Height - 8) = 0) then
      begin
        FMatrixUtilError := True;
        Exit;
      end;
      Matrix.SetInteger(8, Matrix.Height - 8, 1);
    end;
    
    procedure TMatrixUtil.EmbedHorizontalSeparationPattern(XStart, YStart: Integer;
      Matrix: TByteMatrix);
    var
      X: Integer;
    begin
      // We know the width and height.
      for X := 0 to 7 do
      begin
        if (not IsEmpty(Matrix.Get(XStart + X, YStart))) then
        begin
          FMatrixUtilError := True;
          Exit;
        end;
        Matrix.SetInteger(XStart + X, YStart, HORIZONTAL_SEPARATION_PATTERN[0][X]);
      end;
    end;
    
    procedure TMatrixUtil.EmbedVerticalSeparationPattern(XStart, YStart: Integer;
      Matrix: TByteMatrix);
    var
      Y: Integer;
    begin
      // We know the width and height.
      for Y := 0 to 6 do
      begin
        if (not IsEmpty(Matrix.Get(XStart, YStart + Y))) then
        begin
          FMatrixUtilError := True;
          Exit;
        end;
        Matrix.SetInteger(XStart, YStart + Y, VERTICAL_SEPARATION_PATTERN[Y][0]);
      end;
    end;
    
    // Note that we cannot unify the function with embedPositionDetectionPattern() despite they are
    // almost identical, since we cannot write a function that takes 2D arrays in different sizes in
    // C/C++. We should live with the fact.
    procedure TMatrixUtil.EmbedPositionAdjustmentPattern(XStart, YStart: Integer;
      Matrix: TByteMatrix);
    var
      X, Y: Integer;
    begin
      // We know the width and height.
      for Y := 0 to 4 do
      begin
        for X := 0 to 4 do
        begin
          if (not IsEmpty(Matrix.Get(XStart + X, YStart + Y))) then
          begin
            FMatrixUtilError := True;
            Exit;
          end;
          Matrix.SetInteger(XStart + X, YStart + Y,
            POSITION_ADJUSTMENT_PATTERN[Y][X]);
        end;
      end;
    end;
    
    procedure TMatrixUtil.EmbedPositionDetectionPattern(XStart, YStart: Integer;
      Matrix: TByteMatrix);
    var
      X, Y: Integer;
    begin
      // We know the width and height.
      for Y := 0 to 6 do
      begin
        for X := 0 to 6 do
        begin
          if (not IsEmpty(Matrix.Get(XStart + X, YStart + Y))) then
          begin
            FMatrixUtilError := True;
            Exit;
          end;
          Matrix.SetInteger(XStart + X, YStart + Y,
            POSITION_DETECTION_PATTERN[Y][X]);
        end;
      end;
    end;
    
    // Embed position detection patterns and surrounding vertical/horizontal separators.
    procedure TMatrixUtil.EmbedPositionDetectionPatternsAndSeparators
      (Matrix: TByteMatrix);
    var
      PDPWidth: Integer;
      HSPWidth: Integer;
      VSPSize: Integer;
    begin
      // Embed three big squares at corners.
      PDPWidth := Length(POSITION_DETECTION_PATTERN[0]);
      // Left top corner.
      EmbedPositionDetectionPattern(0, 0, Matrix);
      // Right top corner.
      EmbedPositionDetectionPattern(Matrix.Width - PDPWidth, 0, Matrix);
      // Left bottom corner.
      EmbedPositionDetectionPattern(0, Matrix.Width - PDPWidth, Matrix);
    
      // Embed horizontal separation patterns around the squares.
      HSPWidth := Length(HORIZONTAL_SEPARATION_PATTERN[0]);
      // Left top corner.
      EmbedHorizontalSeparationPattern(0, HSPWidth - 1, Matrix);
      // Right top corner.
      EmbedHorizontalSeparationPattern(Matrix.Width - HSPWidth,
        HSPWidth - 1, Matrix);
      // Left bottom corner.
      EmbedHorizontalSeparationPattern(0, Matrix.Width - HSPWidth, Matrix);
    
      // Embed vertical separation patterns around the squares.
      VSPSize := Length(VERTICAL_SEPARATION_PATTERN);
      // Left top corner.
      EmbedVerticalSeparationPattern(VSPSize, 0, Matrix);
      // Right top corner.
      EmbedVerticalSeparationPattern(Matrix.Height - VSPSize - 1, 0, Matrix);
      // Left bottom corner.
      EmbedVerticalSeparationPattern(VSPSize, Matrix.Height - VSPSize, Matrix);
    end;
    
    // Embed position adjustment patterns if need be.
    procedure TMatrixUtil.MaybeEmbedPositionAdjustmentPatterns(Version: Integer;
      Matrix: TByteMatrix);
    var
      Index: Integer;
      Coordinates: array of Integer;
      NumCoordinates: Integer;
      X, Y, I, J: Integer;
    begin
      if (Version >= 2) then
      begin
        Index := Version - 1;
        NumCoordinates :=
          Length(POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[Index]);
        SetLength(Coordinates, NumCoordinates);
        Move(POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[Index][0], Coordinates[0],
          NumCoordinates * SizeOf(Integer));
        for I := 0 to NumCoordinates - 1 do
        begin
          for J := 0 to NumCoordinates - 1 do
          begin
            Y := Coordinates[I];
            X := Coordinates[J];
            if ((X = -1) or (Y = -1)) then
            begin
              Continue;
            end;
            // If the cell is unset, we embed the position adjustment pattern here.
            if (IsEmpty(Matrix.Get(X, Y))) then
            begin
              // -2 is necessary since the x/y coordinates point to the center of the pattern, not the
              // left top corner.
              EmbedPositionAdjustmentPattern(X - 2, Y - 2, Matrix);
            end;
          end;
        end;
      end;
    end;
    
    { TBitArray }
    
    procedure TBitArray.AppendBits(Value, NumBits: Integer);
    var
      NumBitsLeft: Integer;
    begin
      if ((NumBits < 0) or (NumBits > 32)) then
      begin
    
      end;
      EnsureCapacity(Size + NumBits);
      for NumBitsLeft := NumBits downto 1 do
      begin
        AppendBit(((Value shr (NumBitsLeft - 1)) and $01) = 1);
      end;
    end;
    
    constructor TBitArray.Create(Size: Integer);
    
    begin
      Size := Size;
      SetLength(Bits, (Size + 31) shr 5);
    end;
    
    constructor TBitArray.Create;
    begin
      Size := 0;
      SetLength(Bits, 1);
    end;
    
    function TBitArray.Get(I: Integer): Boolean;
    begin
      Result := (Bits[I shr 5] and (1 shl (I and $1F))) <> 0;
    end;
    
    function TBitArray.GetSize: Integer;
    begin
      Result := Size;
    end;
    
    function TBitArray.GetSizeInBytes: Integer;
    begin
      Result := (Size + 7) shr 3;
    end;
    
    procedure TBitArray.SetBit(Index: Integer);
    begin
      Bits[Index shr 5] := Bits[Index shr 5] or (1 shl (Index and $1F));
    end;
    
    procedure TBitArray.AppendBit(Bit: Boolean);
    begin
      EnsureCapacity(Size + 1);
      if (Bit) then
      begin
        Bits[Size shr 5] := Bits[Size shr 5] or (1 shl (Size and $1F));
      end;
      Inc(Size);
    end;
    
    procedure TBitArray.ToBytes(BitOffset: Integer; Source: TByteArray;
      Offset, NumBytes: Integer);
    var
      I: Integer;
      J: Integer;
      TheByte: Integer;
    begin
      for I := 0 to NumBytes - 1 do
      begin
        TheByte := 0;
        for J := 0 to 7 do
        begin
          if (Get(BitOffset)) then
          begin
            TheByte := TheByte or (1 shl (7 - J));
          end;
          Inc(BitOffset);
        end;
        Source[Offset + I] := TheByte;
      end;
    end;
    
    procedure TBitArray.XorOperation(Other: TBitArray);
    var
      I: Integer;
    begin
      if (Length(Bits) = Length(Other.Bits)) then
      begin
        for I := 0 to Length(Bits) - 1 do
        begin
          // The last byte could be incomplete (i.e. not have 8 bits in
          // it) but there is no problem since 0 XOR 0 == 0.
          Bits[I] := Bits[I] xor Other.Bits[I];
        end;
      end;
    end;
    
    procedure TBitArray.AppendBitArray(NewBitArray: TBitArray);
    var
      OtherSize: Integer;
      I: Integer;
    begin
      OtherSize := NewBitArray.GetSize;
      EnsureCapacity(Size + OtherSize);
      for I := 0 to OtherSize - 1 do
      begin
        AppendBit(NewBitArray.Get(I));
      end;
    end;
    
    procedure TBitArray.EnsureCapacity(Size: Integer);
    begin
      if (Size > (Length(Bits) shl 5)) then
      begin
        SetLength(Bits, Size);
      end;
    end;
    
    { TErrorCorrectionLevel }
    
    procedure TErrorCorrectionLevel.Assign(Source: TErrorCorrectionLevel);
    begin
      Self.FBits := Source.FBits;
    end;
    
    function TErrorCorrectionLevel.Ordinal: Integer;
    begin
      Result := 0;
    end;
    
    { TVersion }
    
    class function TVersion.ChooseVersion(NumInputBits: Integer;
      ecLevel: TErrorCorrectionLevel): TVersion;
    var
      VersionNum: Integer;
      Version: TVersion;
      NumBytes: Integer;
      ECBlocks: TECBlocks;
      NumECBytes: Integer;
      NumDataBytes: Integer;
      TotalInputBytes: Integer;
    begin
      Result := nil;
      // In the following comments, we use numbers of Version 7-H.
      for VersionNum := 1 to 40 do
      begin
        Version := TVersion.GetVersionForNumber(VersionNum);
    
        // numBytes = 196
        NumBytes := Version.GetTotalCodewords;
        // getNumECBytes = 130
        ECBlocks := Version.GetECBlocksForLevel(ecLevel);
        NumECBytes := ECBlocks.GetTotalECCodewords;
        // getNumDataBytes = 196 - 130 = 66
        NumDataBytes := NumBytes - NumECBytes;
        TotalInputBytes := (NumInputBits + 7) div 8;
    
        if (NumDataBytes >= TotalInputBytes) then
        begin
          Result := Version;
          Exit;
        end
        else
        begin
          Version.Free;
        end;
      end;
    end;
    
    constructor TVersion.Create(VersionNumber: Integer;
      AlignmentPatternCenters: array of Integer; ECBlocks1, ECBlocks2, ECBlocks3,
      ECBlocks4: TECBlocks);
    var
      Total: Integer;
      ECBlock: TECB;
      ECBArray: TECBArray;
      I: Integer;
    begin
      Self.VersionNumber := VersionNumber;
      SetLength(Self.AlignmentPatternCenters, Length(AlignmentPatternCenters));
      if (Length(AlignmentPatternCenters) > 0) then
      begin
        Move(AlignmentPatternCenters[0], Self.AlignmentPatternCenters[0],
          Length(AlignmentPatternCenters) * SizeOf(Integer));
      end;
      SetLength(ECBlocks, 4);
      ECBlocks[0] := ECBlocks1;
      ECBlocks[1] := ECBlocks2;
      ECBlocks[2] := ECBlocks3;
      ECBlocks[3] := ECBlocks4;
      Total := 0;
      ECCodewords := ECBlocks1.GetECCodewordsPerBlock;
      ECBArray := ECBlocks1.GetECBlocks;
      for I := 0 to Length(ECBArray) - 1 do
      begin
        ECBlock := ECBArray[I];
        Inc(Total, ECBlock.GetCount * (ECBlock.GetDataCodewords + ECCodewords));
      end;
      TotalCodewords := Total;
    end;
    
    destructor TVersion.Destroy;
    var
      X: Integer;
    begin
      for X := 0 to Length(ECBlocks) - 1 do
      begin
        ECBlocks[X].Free;
      end;
      inherited;
    end;
    
    function TVersion.GetDimensionForVersion: Integer;
    begin
      Result := 17 + 4 * VersionNumber;
    end;
    
    function TVersion.GetECBlocksForLevel(ecLevel: TErrorCorrectionLevel)
      : TECBlocks;
    begin
      Result := ECBlocks[ecLevel.Ordinal];
    end;
    
    function TVersion.GetTotalCodewords: Integer;
    begin
      Result := TotalCodewords;
    end;
    
    class function TVersion.GetVersionForNumber(VersionNum: Integer): TVersion;
    begin
      if (VersionNum = 1) then
      begin
        Result := TVersion.Create(1, [], TECBlocks.Create(7, TECB.Create(1, 19)),
          TECBlocks.Create(10, TECB.Create(1, 16)),
          TECBlocks.Create(13, TECB.Create(1, 13)),
          TECBlocks.Create(17, TECB.Create(1, 9)));
      end
      else if (VersionNum = 2) then
      begin
        Result := TVersion.Create(2, [6, 18],
          TECBlocks.Create(10, TECB.Create(1, 34)),
          TECBlocks.Create(16, TECB.Create(1, 28)),
          TECBlocks.Create(22, TECB.Create(1, 22)),
          TECBlocks.Create(28, TECB.Create(1, 16)));
      end
      else if (VersionNum = 3) then
      begin
        Result := TVersion.Create(3, [6, 22],
          TECBlocks.Create(15, TECB.Create(1, 55)),
          TECBlocks.Create(26, TECB.Create(1, 44)),
          TECBlocks.Create(18, TECB.Create(2, 17)),
          TECBlocks.Create(22, TECB.Create(2, 13)));
      end
      else if (VersionNum = 4) then
      begin
        Result := TVersion.Create(4, [6, 26],
          TECBlocks.Create(20, TECB.Create(1, 80)),
          TECBlocks.Create(18, TECB.Create(2, 32)),
          TECBlocks.Create(26, TECB.Create(2, 24)),
          TECBlocks.Create(16, TECB.Create(4, 9)));
      end
      else if (VersionNum = 5) then
      begin
        Result := TVersion.Create(5, [6, 30],
          TECBlocks.Create(26, TECB.Create(1, 108)),
          TECBlocks.Create(24, TECB.Create(2, 43)),
          TECBlocks.Create(18, TECB.Create(2, 15), TECB.Create(2, 16)),
          TECBlocks.Create(22, TECB.Create(2, 11), TECB.Create(2, 12)));
      end
      else if (VersionNum = 6) then
      begin
        Result := TVersion.Create(6, [6, 34],
          TECBlocks.Create(18, TECB.Create(2, 68)),
          TECBlocks.Create(16, TECB.Create(4, 27)),
          TECBlocks.Create(24, TECB.Create(4, 19)),
          TECBlocks.Create(28, TECB.Create(4, 15)));
      end
      else if (VersionNum = 7) then
      begin
        Result := TVersion.Create(7, [6, 22, 38],
          TECBlocks.Create(20, TECB.Create(2, 78)),
          TECBlocks.Create(18, TECB.Create(4, 31)),
          TECBlocks.Create(18, TECB.Create(2, 14), TECB.Create(4, 15)),
          TECBlocks.Create(26, TECB.Create(4, 13), TECB.Create(1, 14)));
      end
      else if (VersionNum = 8) then
      begin
        Result := TVersion.Create(8, [6, 24, 42],
          TECBlocks.Create(24, TECB.Create(2, 97)),
          TECBlocks.Create(22, TECB.Create(2, 38), TECB.Create(2, 39)),
          TECBlocks.Create(22, TECB.Create(4, 18), TECB.Create(2, 19)),
          TECBlocks.Create(26, TECB.Create(4, 14), TECB.Create(2, 15)));
      end
      else if (VersionNum = 9) then
      begin
        Result := TVersion.Create(9, [6, 26, 46],
          TECBlocks.Create(30, TECB.Create(2, 116)),
          TECBlocks.Create(22, TECB.Create(3, 36), TECB.Create(2, 37)),
          TECBlocks.Create(20, TECB.Create(4, 16), TECB.Create(4, 17)),
          TECBlocks.Create(24, TECB.Create(4, 12), TECB.Create(4, 13)));
      end
      else if (VersionNum = 10) then
      begin
        Result := TVersion.Create(10, [6, 28, 50],
          TECBlocks.Create(18, TECB.Create(2, 68), TECB.Create(2, 69)),
          TECBlocks.Create(26, TECB.Create(4, 43), TECB.Create(1, 44)),
          TECBlocks.Create(24, TECB.Create(6, 19), TECB.Create(2, 20)),
          TECBlocks.Create(28, TECB.Create(6, 15), TECB.Create(2, 16)));
      end
      else if (VersionNum = 11) then
      begin
        Result := TVersion.Create(11, [6, 30, 54],
          TECBlocks.Create(20, TECB.Create(4, 81)),
          TECBlocks.Create(30, TECB.Create(1, 50), TECB.Create(4, 51)),
          TECBlocks.Create(28, TECB.Create(4, 22), TECB.Create(4, 23)),
          TECBlocks.Create(24, TECB.Create(3, 12), TECB.Create(8, 13)));
      end
      else if (VersionNum = 12) then
      begin
        Result := TVersion.Create(12, [6, 32, 58],
          TECBlocks.Create(24, TECB.Create(2, 92), TECB.Create(2, 93)),
          TECBlocks.Create(22, TECB.Create(6, 36), TECB.Create(2, 37)),
          TECBlocks.Create(26, TECB.Create(4, 20), TECB.Create(6, 21)),
          TECBlocks.Create(28, TECB.Create(7, 14), TECB.Create(4, 15)));
      end
      else if (VersionNum = 13) then
      begin
        Result := TVersion.Create(13, [6, 34, 62],
          TECBlocks.Create(26, TECB.Create(4, 107)),
          TECBlocks.Create(22, TECB.Create(8, 37), TECB.Create(1, 38)),
          TECBlocks.Create(24, TECB.Create(8, 20), TECB.Create(4, 21)),
          TECBlocks.Create(22, TECB.Create(12, 11), TECB.Create(4, 12)));
      end
      else if (VersionNum = 14) then
      begin
        Result := TVersion.Create(14, [6, 26, 46, 66],
          TECBlocks.Create(30, TECB.Create(3, 115), TECB.Create(1, 116)),
          TECBlocks.Create(24, TECB.Create(4, 40), TECB.Create(5, 41)),
          TECBlocks.Create(20, TECB.Create(11, 16), TECB.Create(5, 17)),
          TECBlocks.Create(24, TECB.Create(11, 12), TECB.Create(5, 13)));
      end
      else if (VersionNum = 15) then
      begin
        Result := TVersion.Create(15, [6, 26, 48, 70],
          TECBlocks.Create(22, TECB.Create(5, 87), TECB.Create(1, 88)),
          TECBlocks.Create(24, TECB.Create(5, 41), TECB.Create(5, 42)),
          TECBlocks.Create(30, TECB.Create(5, 24), TECB.Create(7, 25)),
          TECBlocks.Create(24, TECB.Create(11, 12), TECB.Create(7, 13)));
      end
      else if (VersionNum = 16) then
      begin
        Result := TVersion.Create(16, [6, 26, 50, 74],
          TECBlocks.Create(24, TECB.Create(5, 98), TECB.Create(1, 99)),
          TECBlocks.Create(28, TECB.Create(7, 45), TECB.Create(3, 46)),
          TECBlocks.Create(24, TECB.Create(15, 19), TECB.Create(2, 20)),
          TECBlocks.Create(30, TECB.Create(3, 15), TECB.Create(13, 16)));
      end
      else if (VersionNum = 17) then
      begin
        Result := TVersion.Create(17, [6, 30, 54, 78],
          TECBlocks.Create(28, TECB.Create(1, 107), TECB.Create(5, 108)),
          TECBlocks.Create(28, TECB.Create(10, 46), TECB.Create(1, 47)),
          TECBlocks.Create(28, TECB.Create(1, 22), TECB.Create(15, 23)),
          TECBlocks.Create(28, TECB.Create(2, 14), TECB.Create(17, 15)));
      end
      else if (VersionNum = 18) then
      begin
        Result := TVersion.Create(18, [6, 30, 56, 82],
          TECBlocks.Create(30, TECB.Create(5, 120), TECB.Create(1, 121)),
          TECBlocks.Create(26, TECB.Create(9, 43), TECB.Create(4, 44)),
          TECBlocks.Create(28, TECB.Create(17, 22), TECB.Create(1, 23)),
          TECBlocks.Create(28, TECB.Create(2, 14), TECB.Create(19, 15)));
      end
      else if (VersionNum = 19) then
      begin
        Result := TVersion.Create(19, [6, 30, 58, 86],
          TECBlocks.Create(28, TECB.Create(3, 113), TECB.Create(4, 114)),
          TECBlocks.Create(26, TECB.Create(3, 44), TECB.Create(11, 45)),
          TECBlocks.Create(26, TECB.Create(17, 21), TECB.Create(4, 22)),
          TECBlocks.Create(26, TECB.Create(9, 13), TECB.Create(16, 14)));
      end
      else if (VersionNum = 20) then
      begin
        Result := TVersion.Create(20, [6, 34, 62, 90],
          TECBlocks.Create(28, TECB.Create(3, 107), TECB.Create(5, 108)),
          TECBlocks.Create(26, TECB.Create(3, 41), TECB.Create(13, 42)),
          TECBlocks.Create(30, TECB.Create(15, 24), TECB.Create(5, 25)),
          TECBlocks.Create(28, TECB.Create(15, 15), TECB.Create(10, 16)));
      end
      else if (VersionNum = 21) then
      begin
        Result := TVersion.Create(21, [6, 28, 50, 72, 94],
          TECBlocks.Create(28, TECB.Create(4, 116), TECB.Create(4, 117)),
          TECBlocks.Create(26, TECB.Create(17, 42)),
          TECBlocks.Create(28, TECB.Create(17, 22), TECB.Create(6, 23)),
          TECBlocks.Create(30, TECB.Create(19, 16), TECB.Create(6, 17)));
      end
      else if (VersionNum = 22) then
      begin
        Result := TVersion.Create(22, [6, 26, 50, 74, 98],
          TECBlocks.Create(28, TECB.Create(2, 111), TECB.Create(7, 112)),
          TECBlocks.Create(28, TECB.Create(17, 46)),
          TECBlocks.Create(30, TECB.Create(7, 24), TECB.Create(16, 25)),
          TECBlocks.Create(24, TECB.Create(34, 13)));
      end
      else if (VersionNum = 23) then
      begin
        Result := TVersion.Create(23, [6, 30, 54, 78, 102],
          TECBlocks.Create(30, TECB.Create(4, 121), TECB.Create(5, 122)),
          TECBlocks.Create(28, TECB.Create(4, 47), TECB.Create(14, 48)),
          TECBlocks.Create(30, TECB.Create(11, 24), TECB.Create(14, 25)),
          TECBlocks.Create(30, TECB.Create(16, 15), TECB.Create(14, 16)));
      end
      else if (VersionNum = 24) then
      begin
        Result := TVersion.Create(24, [6, 28, 54, 80, 106],
          TECBlocks.Create(30, TECB.Create(6, 117), TECB.Create(4, 118)),
          TECBlocks.Create(28, TECB.Create(6, 45), TECB.Create(14, 46)),
          TECBlocks.Create(30, TECB.Create(11, 24), TECB.Create(16, 25)),
          TECBlocks.Create(30, TECB.Create(30, 16), TECB.Create(2, 17)));
      end
      else if (VersionNum = 25) then
      begin
        Result := TVersion.Create(25, [6, 32, 58, 84, 110],
          TECBlocks.Create(26, TECB.Create(8, 106), TECB.Create(4, 107)),
          TECBlocks.Create(28, TECB.Create(8, 47), TECB.Create(13, 48)),
          TECBlocks.Create(30, TECB.Create(7, 24), TECB.Create(22, 25)),
          TECBlocks.Create(30, TECB.Create(22, 15), TECB.Create(13, 16)));
      end
      else if (VersionNum = 26) then
      begin
        Result := TVersion.Create(26, [6, 30, 58, 86, 114],
          TECBlocks.Create(28, TECB.Create(10, 114), TECB.Create(2, 115)),
          TECBlocks.Create(28, TECB.Create(19, 46), TECB.Create(4, 47)),
          TECBlocks.Create(28, TECB.Create(28, 22), TECB.Create(6, 23)),
          TECBlocks.Create(30, TECB.Create(33, 16), TECB.Create(4, 17)));
      end
      else if (VersionNum = 27) then
      begin
        Result := TVersion.Create(27, [6, 34, 62, 90, 118],
          TECBlocks.Create(30, TECB.Create(8, 122), TECB.Create(4, 123)),
          TECBlocks.Create(28, TECB.Create(22, 45), TECB.Create(3, 46)),
          TECBlocks.Create(30, TECB.Create(8, 23), TECB.Create(26, 24)),
          TECBlocks.Create(30, TECB.Create(12, 15), TECB.Create(28, 16)));
      end
      else if (VersionNum = 28) then
      begin
        Result := TVersion.Create(28, [6, 26, 50, 74, 98, 122],
          TECBlocks.Create(30, TECB.Create(3, 117), TECB.Create(10, 118)),
          TECBlocks.Create(28, TECB.Create(3, 45), TECB.Create(23, 46)),
          TECBlocks.Create(30, TECB.Create(4, 24), TECB.Create(31, 25)),
          TECBlocks.Create(30, TECB.Create(11, 15), TECB.Create(31, 16)));
      end
      else if (VersionNum = 29) then
      begin
        Result := TVersion.Create(29, [6, 30, 54, 78, 102, 126],
          TECBlocks.Create(30, TECB.Create(7, 116), TECB.Create(7, 117)),
          TECBlocks.Create(28, TECB.Create(21, 45), TECB.Create(7, 46)),
          TECBlocks.Create(30, TECB.Create(1, 23), TECB.Create(37, 24)),
          TECBlocks.Create(30, TECB.Create(19, 15), TECB.Create(26, 16)));
      end
      else if (VersionNum = 30) then
      begin
        Result := TVersion.Create(30, [6, 26, 52, 78, 104, 130],
          TECBlocks.Create(30, TECB.Create(5, 115), TECB.Create(10, 116)),
          TECBlocks.Create(28, TECB.Create(19, 47), TECB.Create(10, 48)),
          TECBlocks.Create(30, TECB.Create(15, 24), TECB.Create(25, 25)),
          TECBlocks.Create(30, TECB.Create(23, 15), TECB.Create(25, 16)));
      end
      else if (VersionNum = 31) then
      begin
        Result := TVersion.Create(31, [6, 30, 56, 82, 108, 134],
          TECBlocks.Create(30, TECB.Create(13, 115), TECB.Create(3, 116)),
          TECBlocks.Create(28, TECB.Create(2, 46), TECB.Create(29, 47)),
          TECBlocks.Create(30, TECB.Create(42, 24), TECB.Create(1, 25)),
          TECBlocks.Create(30, TECB.Create(23, 15), TECB.Create(28, 16)));
      end
      else if (VersionNum = 32) then
      begin
        Result := TVersion.Create(32, [6, 34, 60, 86, 112, 138],
          TECBlocks.Create(30, TECB.Create(17, 115)),
          TECBlocks.Create(28, TECB.Create(10, 46), TECB.Create(23, 47)),
          TECBlocks.Create(30, TECB.Create(10, 24), TECB.Create(35, 25)),
          TECBlocks.Create(30, TECB.Create(19, 15), TECB.Create(35, 16)));
      end
      else if (VersionNum = 33) then
      begin
        Result := TVersion.Create(33, [6, 30, 58, 86, 114, 142],
          TECBlocks.Create(30, TECB.Create(17, 115), TECB.Create(1, 116)),
          TECBlocks.Create(28, TECB.Create(14, 46), TECB.Create(21, 47)),
          TECBlocks.Create(30, TECB.Create(29, 24), TECB.Create(19, 25)),
          TECBlocks.Create(30, TECB.Create(11, 15), TECB.Create(46, 16)));
      end
      else if (VersionNum = 34) then
      begin
        Result := TVersion.Create(34, [6, 34, 62, 90, 118, 146],
          TECBlocks.Create(30, TECB.Create(13, 115), TECB.Create(6, 116)),
          TECBlocks.Create(28, TECB.Create(14, 46), TECB.Create(23, 47)),
          TECBlocks.Create(30, TECB.Create(44, 24), TECB.Create(7, 25)),
          TECBlocks.Create(30, TECB.Create(59, 16), TECB.Create(1, 17)));
      end
      else if (VersionNum = 35) then
      begin
        Result := TVersion.Create(35, [6, 30, 54, 78, 102, 126, 150],
          TECBlocks.Create(30, TECB.Create(12, 121), TECB.Create(7, 122)),
          TECBlocks.Create(28, TECB.Create(12, 47), TECB.Create(26, 48)),
          TECBlocks.Create(30, TECB.Create(39, 24), TECB.Create(14, 25)),
          TECBlocks.Create(30, TECB.Create(22, 15), TECB.Create(41, 16)));
      end
      else if (VersionNum = 36) then
      begin
        Result := TVersion.Create(36, [6, 24, 50, 76, 102, 128, 154],
          TECBlocks.Create(30, TECB.Create(6, 121), TECB.Create(14, 122)),
          TECBlocks.Create(28, TECB.Create(6, 47), TECB.Create(34, 48)),
          TECBlocks.Create(30, TECB.Create(46, 24), TECB.Create(10, 25)),
          TECBlocks.Create(30, TECB.Create(2, 15), TECB.Create(64, 16)));
      end
      else if (VersionNum = 37) then
      begin
        Result := TVersion.Create(37, [6, 28, 54, 80, 106, 132, 158],
          TECBlocks.Create(30, TECB.Create(17, 122), TECB.Create(4, 123)),
          TECBlocks.Create(28, TECB.Create(29, 46), TECB.Create(14, 47)),
          TECBlocks.Create(30, TECB.Create(49, 24), TECB.Create(10, 25)),
          TECBlocks.Create(30, TECB.Create(24, 15), TECB.Create(46, 16)));
      end
      else if (VersionNum = 38) then
      begin
        Result := TVersion.Create(38, [6, 32, 58, 84, 110, 136, 162],
          TECBlocks.Create(30, TECB.Create(4, 122), TECB.Create(18, 123)),
          TECBlocks.Create(28, TECB.Create(13, 46), TECB.Create(32, 47)),
          TECBlocks.Create(30, TECB.Create(48, 24), TECB.Create(14, 25)),
          TECBlocks.Create(30, TECB.Create(42, 15), TECB.Create(32, 16)));
      end
      else if (VersionNum = 39) then
      begin
        Result := TVersion.Create(39, [6, 26, 54, 82, 110, 138, 166],
          TECBlocks.Create(30, TECB.Create(20, 117), TECB.Create(4, 118)),
          TECBlocks.Create(28, TECB.Create(40, 47), TECB.Create(7, 48)),
          TECBlocks.Create(30, TECB.Create(43, 24), TECB.Create(22, 25)),
          TECBlocks.Create(30, TECB.Create(10, 15), TECB.Create(67, 16)));
      end
      else if (VersionNum = 40) then
      begin
        Result := TVersion.Create(40, [6, 30, 58, 86, 114, 142, 170],
          TECBlocks.Create(30, TECB.Create(19, 118), TECB.Create(6, 119)),
          TECBlocks.Create(28, TECB.Create(18, 47), TECB.Create(31, 48)),
          TECBlocks.Create(30, TECB.Create(34, 24), TECB.Create(34, 25)),
          TECBlocks.Create(30, TECB.Create(20, 15), TECB.Create(61, 16)));
      end
      else
      begin
        Result := nil;
      end;
    end;
    
    { TMaskUtil }
    
    // Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
    // pattern conditions.
    function TMaskUtil.GetDataMaskBit(MaskPattern, X, Y: Integer): Boolean;
    var
      Intermediate: Integer;
      Temp: Integer;
    begin
      Intermediate := 0;
      if ((MaskPattern >= 0) and (MaskPattern < NUM_MASK_PATTERNS)) then
      begin
        case (MaskPattern) of
          0:
            Intermediate := (Y + X) and 1;
          1:
            Intermediate := Y and 1;
          2:
            Intermediate := X mod 3;
          3:
            Intermediate := (Y + X) mod 3;
          4:
            Intermediate := ((Y shr 1) + (X div 3)) and 1;
          5:
            begin
              Temp := Y * X;
              Intermediate := (Temp and 1) + (Temp mod 3);
            end;
          6:
            begin
              Temp := Y * X;
              Intermediate := ((Temp and 1) + (Temp mod 3)) and 1;
            end;
          7:
            begin
              Temp := Y * X;
              Intermediate := ((Temp mod 3) + ((Y + X) and 1)) and 1;
            end;
        end;
      end;
      Result := Intermediate = 0;
    end;
    
    { TECBlocks }
    
    constructor TECBlocks.Create(ECCodewordsPerBlock: Integer; ECBlocks: TECB);
    begin
      Self.ECCodewordsPerBlock := ECCodewordsPerBlock;
      SetLength(Self.ECBlocks, 1);
      Self.ECBlocks[0] := ECBlocks;
    end;
    
    constructor TECBlocks.Create(ECCodewordsPerBlock: Integer;
      ECBlocks1, ECBlocks2: TECB);
    begin
      Self.ECCodewordsPerBlock := ECCodewordsPerBlock;
      SetLength(Self.ECBlocks, 2);
      ECBlocks[0] := ECBlocks1;
      ECBlocks[1] := ECBlocks2;
    end;
    
    destructor TECBlocks.Destroy;
    var
      X: Integer;
    begin
      for X := 0 to Length(ECBlocks) - 1 do
      begin
        ECBlocks[X].Free;
      end;
      inherited;
    end;
    
    function TECBlocks.GetECBlocks: TECBArray;
    begin
      Result := ECBlocks;
    end;
    
    function TECBlocks.GetECCodewordsPerBlock: Integer;
    begin
      Result := ECCodewordsPerBlock;
    end;
    
    function TECBlocks.GetNumBlocks: Integer;
    var
      Total: Integer;
      I: Integer;
    begin
      Total := 0;
      for I := 0 to Length(ECBlocks) - 1 do
      begin
        Inc(Total, ECBlocks[I].GetCount);
      end;
      Result := Total;
    end;
    
    function TECBlocks.GetTotalECCodewords: Integer;
    begin
      Result := ECCodewordsPerBlock * GetNumBlocks;
    end;
    
    { TBlockPair }
    
    constructor TBlockPair.Create(BA1, BA2: TByteArray);
    begin
      FDataBytes := BA1;
      FErrorCorrectionBytes := BA2;
    end;
    
    function TBlockPair.GetDataBytes: TByteArray;
    begin
      Result := FDataBytes;
    end;
    
    function TBlockPair.GetErrorCorrectionBytes: TByteArray;
    begin
      Result := FErrorCorrectionBytes;
    end;
    
    { TReedSolomonEncoder }
    
    function TReedSolomonEncoder.BuildGenerator(Degree: Integer): TGenericGFPoly;
    var
      LastGenerator: TGenericGFPoly;
      NextGenerator: TGenericGFPoly;
      Poly: TGenericGFPoly;
      D: Integer;
      CA: TIntegerArray;
    begin
      if (Degree >= FCachedGenerators.Count) then
      begin
        LastGenerator := TGenericGFPoly
          (FCachedGenerators[FCachedGenerators.Count - 1]);
    
        for D := FCachedGenerators.Count to Degree do
        begin
          SetLength(CA, 2);
          CA[0] := 1;
          CA[1] := FField.Exp(D - 1 + FField.GetGeneratorBase);
          Poly := TGenericGFPoly.Create(FField, CA);
          NextGenerator := LastGenerator.Multiply(Poly);
          FCachedGenerators.Add(NextGenerator);
          LastGenerator := NextGenerator;
        end;
      end;
      Result := TGenericGFPoly(FCachedGenerators[Degree]);
    end;
    
    constructor TReedSolomonEncoder.Create(AField: TGenericGF);
    var
      GenericGFPoly: TGenericGFPoly;
      IntArray: TIntegerArray;
    begin
      FField := AField;
    
      // Contents of FCachedGenerators will be freed by FGenericGF.Destroy
      FCachedGenerators := TObjectList<TGenericGFPoly>.Create(False);
    
      SetLength(IntArray, 1);
      IntArray[0] := 1;
      GenericGFPoly := TGenericGFPoly.Create(AField, IntArray);
      FCachedGenerators.Add(GenericGFPoly);
    end;
    
    destructor TReedSolomonEncoder.Destroy;
    begin
      FCachedGenerators.Free;
      inherited;
    end;
    
    procedure TReedSolomonEncoder.Encode(ToEncode: TIntegerArray; ECBytes: Integer);
    var
      DataBytes: Integer;
      Generator: TGenericGFPoly;
      InfoCoefficients: TIntegerArray;
      Info: TGenericGFPoly;
      Remainder: TGenericGFPoly;
      Coefficients: TIntegerArray;
      NumZeroCoefficients: Integer;
      I: Integer;
    begin
      SetLength(Coefficients, 0);
      if (ECBytes > 0) then
      begin
        DataBytes := Length(ToEncode) - ECBytes;
        if (DataBytes > 0) then
        begin
          Generator := BuildGenerator(ECBytes);
          SetLength(InfoCoefficients, DataBytes);
          InfoCoefficients := Copy(ToEncode, 0, DataBytes);
          Info := TGenericGFPoly.Create(FField, InfoCoefficients);
          Info := Info.MultiplyByMonomial(ECBytes, 1);
          Remainder := Info.Divide(Generator)[1];
          Coefficients := Remainder.GetCoefficients;
          NumZeroCoefficients := ECBytes - Length(Coefficients);
          for I := 0 to NumZeroCoefficients - 1 do
          begin
            ToEncode[DataBytes + I] := 0;
          end;
          Move(Coefficients[0], ToEncode[DataBytes + NumZeroCoefficients],
            Length(Coefficients) * SizeOf(Integer));
        end;
      end;
    end;
    
    { TECB }
    
    constructor TECB.Create(Count, DataCodewords: Integer);
    begin
      Self.Count := Count;
      Self.DataCodewords := DataCodewords;
    end;
    
    function TECB.GetCount: Integer;
    begin
      Result := Count;
    end;
    
    function TECB.GetDataCodewords: Integer;
    begin
      Result := DataCodewords;
    end;
    
    { TGenericGFPoly }
    
    function TGenericGFPoly.AddOrSubtract(Other: TGenericGFPoly): TGenericGFPoly;
    var
      SmallerCoefficients: TIntegerArray;
      LargerCoefficients: TIntegerArray;
      Temp: TIntegerArray;
      SumDiff: TIntegerArray;
      LengthDiff: Integer;
      I: Integer;
    begin
      SetLength(SmallerCoefficients, 0);
      SetLength(LargerCoefficients, 0);
      SetLength(Temp, 0);
      SetLength(SumDiff, 0);
    
      Result := nil;
      if (Assigned(Other)) then
      begin
        if (FField = Other.FField) then
        begin
          if (IsZero) then
          begin
            Result := Other;
            Exit;
          end;
    
          if (Other.IsZero) then
          begin
            Result := Self;
            Exit;
          end;
    
          SmallerCoefficients := FCoefficients;
          LargerCoefficients := Other.Coefficients;
          if (Length(SmallerCoefficients) > Length(LargerCoefficients)) then
          begin
            Temp := SmallerCoefficients;
            SmallerCoefficients := LargerCoefficients;
            LargerCoefficients := Temp;
          end;
          SetLength(SumDiff, Length(LargerCoefficients));
          LengthDiff := Length(LargerCoefficients) - Length(SmallerCoefficients);
    
          // Copy high-order terms only found in higher-degree polynomial's coefficients
          if (LengthDiff > 0) then
          begin
            // SumDiff := Copy(LargerCoefficients, 0, LengthDiff);
            Move(LargerCoefficients[0], SumDiff[0], LengthDiff * SizeOf(Integer));
          end;
    
          for I := LengthDiff to Length(LargerCoefficients) - 1 do
          begin
            SumDiff[I] := TGenericGF.AddOrSubtract
              (SmallerCoefficients[I - LengthDiff], LargerCoefficients[I]);
          end;
    
          Result := TGenericGFPoly.Create(FField, SumDiff);
        end;
      end;
    end;
    
    function TGenericGFPoly.Coefficients: TIntegerArray;
    begin
      Result := FCoefficients;
    end;
    
    constructor TGenericGFPoly.Create(AField: TGenericGF;
      ACoefficients: TIntegerArray);
    var
      CoefficientsLength: Integer;
      FirstNonZero: Integer;
    begin
      FField := AField;
      SetLength(FField.FPolyList, Length(FField.FPolyList) + 1);
      FField.FPolyList[Length(FField.FPolyList) - 1] := Self;
      CoefficientsLength := Length(ACoefficients);
      if ((CoefficientsLength > 1) and (ACoefficients[0] = 0)) then
      begin
        // Leading term must be non-zero for anything except the constant polynomial "0"
        FirstNonZero := 1;
        while ((FirstNonZero < CoefficientsLength) and
          (ACoefficients[FirstNonZero] = 0)) do
        begin
          Inc(FirstNonZero);
        end;
    
        if (FirstNonZero = CoefficientsLength) then
        begin
          FCoefficients := AField.GetZero.Coefficients;
        end
        else
        begin
          SetLength(FCoefficients, CoefficientsLength - FirstNonZero);
          FCoefficients := Copy(ACoefficients, FirstNonZero, Length(FCoefficients));
        end;
      end
      else
      begin
        FCoefficients := ACoefficients;
      end;
    end;
    
    destructor TGenericGFPoly.Destroy;
    begin
      Self.FField := FField;
      inherited;
    end;
    
    function TGenericGFPoly.Divide(Other: TGenericGFPoly): TGenericGFPolyArray;
    var
      Quotient: TGenericGFPoly;
      Remainder: TGenericGFPoly;
      DenominatorLeadingTerm: Integer;
      InverseDenominatorLeadingTerm: Integer;
      DegreeDifference: Integer;
      Scale: Integer;
      Term: TGenericGFPoly;
      IterationQuotient: TGenericGFPoly;
    begin
      SetLength(Result, 0);
      if ((FField = Other.FField) and (not Other.IsZero)) then
      begin
    
        Quotient := FField.GetZero;
        Remainder := Self;
    
        DenominatorLeadingTerm := Other.GetCoefficient(Other.GetDegree);
        InverseDenominatorLeadingTerm := FField.Inverse(DenominatorLeadingTerm);
    
        while ((Remainder.GetDegree >= Other.GetDegree) and
          (not Remainder.IsZero)) do
        begin
          DegreeDifference := Remainder.GetDegree - Other.GetDegree;
          Scale := FField.Multiply(Remainder.GetCoefficient(Remainder.GetDegree),
            InverseDenominatorLeadingTerm);
          Term := Other.MultiplyByMonomial(DegreeDifference, Scale);
          IterationQuotient := FField.BuildMonomial(DegreeDifference, Scale);
          Quotient := Quotient.AddOrSubtract(IterationQuotient);
          Remainder := Remainder.AddOrSubtract(Term);
        end;
    
        SetLength(Result, 2);
        Result[0] := Quotient;
        Result[1] := Remainder;
      end;
    end;
    
    function TGenericGFPoly.GetCoefficient(Degree: Integer): Integer;
    begin
      Result := FCoefficients[Length(FCoefficients) - 1 - Degree];
    end;
    
    function TGenericGFPoly.GetCoefficients: TIntegerArray;
    begin
      Result := FCoefficients;
    end;
    
    function TGenericGFPoly.GetDegree: Integer;
    begin
      Result := Length(FCoefficients) - 1;
    end;
    
    function TGenericGFPoly.IsZero: Boolean;
    begin
      Result := FCoefficients[0] = 0;
    end;
    
    function TGenericGFPoly.Multiply(Other: TGenericGFPoly): TGenericGFPoly;
    var
      ACoefficients: TIntegerArray;
      BCoefficients: TIntegerArray;
      Product: TIntegerArray;
      ALength: Integer;
      BLength: Integer;
      I: Integer;
      J: Integer;
      ACoeff: Integer;
    begin
      SetLength(ACoefficients, 0);
      SetLength(BCoefficients, 0);
      Result := nil;
    
      if (FField = Other.FField) then
      begin
        if (IsZero or Other.IsZero) then
        begin
          Result := FField.GetZero;
          Exit;
        end;
    
        ACoefficients := FCoefficients;
        ALength := Length(ACoefficients);
        BCoefficients := Other.Coefficients;
        BLength := Length(BCoefficients);
        SetLength(Product, ALength + BLength - 1);
        for I := 0 to ALength - 1 do
        begin
          ACoeff := ACoefficients[I];
          for J := 0 to BLength - 1 do
          begin
            Product[I + J] := TGenericGF.AddOrSubtract(Product[I + J],
              FField.Multiply(ACoeff, BCoefficients[J]));
          end;
        end;
        Result := TGenericGFPoly.Create(FField, Product);
      end;
    end;
    
    function TGenericGFPoly.MultiplyByMonomial(Degree, Coefficient: Integer)
      : TGenericGFPoly;
    var
      I: Integer;
      Size: Integer;
      Product: TIntegerArray;
    begin
      Result := nil;
      if (Degree >= 0) then
      begin
        if (Coefficient = 0) then
        begin
          Result := FField.GetZero;
          Exit;
        end;
        Size := Length(Coefficients);
        SetLength(Product, Size + Degree);
        for I := 0 to Size - 1 do
        begin
          Product[I] := FField.Multiply(FCoefficients[I], Coefficient);
        end;
        Result := TGenericGFPoly.Create(FField, Product);
      end;
    end;
    
    { TGenericGF }
    
    class function TGenericGF.AddOrSubtract(A, B: Integer): Integer;
    begin
      Result := A xor B;
    end;
    
    function TGenericGF.BuildMonomial(Degree, Coefficient: Integer): TGenericGFPoly;
    var
      Coefficients: TIntegerArray;
    begin
      CheckInit();
    
      if (Degree >= 0) then
      begin
        if (Coefficient = 0) then
        begin
          Result := FZero;
          Exit;
        end;
        SetLength(Coefficients, Degree + 1);
        Coefficients[0] := Coefficient;
        Result := TGenericGFPoly.Create(Self, Coefficients);
      end
      else
      begin
        Result := nil;
      end;
    end;
    
    procedure TGenericGF.CheckInit;
    begin
      if (not FInitialized) then
      begin
        Initialize;
      end;
    end;
    
    constructor TGenericGF.Create(Primitive, Size, B: Integer);
    begin
      FInitialized := False;
      FPrimitive := Primitive;
      FSize := Size;
      FGeneratorBase := B;
      if (FSize < 0) then
      begin
        Initialize;
      end;
    end;
    
    class function TGenericGF.CreateQRCodeField256: TGenericGF;
    begin
      Result := TGenericGF.Create($011D, 256, 0);
    end;
    
    destructor TGenericGF.Destroy;
    var
      X: Integer;
      Y: Integer;
    begin
      for X := 0 to Length(FPolyList) - 1 do
      begin
        if (Assigned(FPolyList[X])) then
        begin
          for Y := X + 1 to Length(FPolyList) - 1 do
          begin
            if (FPolyList[Y] = FPolyList[X]) then
            begin
              FPolyList[Y] := nil;
            end;
          end;
          FPolyList[X].Free;
        end;
      end;
      inherited;
    end;
    
    function TGenericGF.Exp(A: Integer): Integer;
    begin
      CheckInit;
      Result := FExpTable[A];
    end;
    
    function TGenericGF.GetGeneratorBase: Integer;
    begin
      Result := FGeneratorBase;
    end;
    
    function TGenericGF.GetZero: TGenericGFPoly;
    begin
      CheckInit;
      Result := FZero;
    end;
    
    procedure TGenericGF.Initialize;
    var
      X: Integer;
      I: Integer;
      CA: TIntegerArray;
    begin
      SetLength(FExpTable, FSize);
      SetLength(FLogTable, FSize);
      X := 1;
      for I := 0 to FSize - 1 do
      begin
        FExpTable[I] := X;
        X := X shl 1; // x = x * 2; we're assuming the generator alpha is 2
        if (X >= FSize) then
        begin
          X := X xor FPrimitive;
          X := X and (FSize - 1);
        end;
      end;
    
      for I := 0 to FSize - 2 do
      begin
        FLogTable[FExpTable[I]] := I;
      end;
    
      // logTable[0] == 0 but this should never be used
    
      SetLength(CA, 1);
      CA[0] := 0;
      FZero := TGenericGFPoly.Create(Self, CA);
    
      SetLength(CA, 1);
      CA[0] := 1;
      FOne := TGenericGFPoly.Create(Self, CA);
    
      FInitialized := True;
    end;
    
    function TGenericGF.Inverse(A: Integer): Integer;
    begin
      CheckInit;
    
      if (A <> 0) then
      begin
        Result := FExpTable[FSize - FLogTable[A] - 1];
      end
      else
      begin
        Result := 0;
      end;
    end;
    
    function TGenericGF.Multiply(A, B: Integer): Integer;
    begin
      CheckInit;
      if ((A <> 0) and (B <> 0)) then
      begin
        Result := FExpTable[(FLogTable[A] + FLogTable[B]) mod (FSize - 1)];
      end
      else
      begin
        Result := 0;
      end;
    end;
    
    function GenerateQRCode(const Input: string; EncodeOptions: Integer)
      : T2DBooleanArray;
    var
      Encoder: TEncoder;
      Level: TErrorCorrectionLevel;
      QRCode: TQRCode;
      X: Integer;
      Y: Integer;
    begin
      Level := TErrorCorrectionLevel.Create;
      Level.FBits := 1;
      Encoder := TEncoder.Create;
      QRCode := TQRCode.Create;
      try
        Encoder.Encode(Input, EncodeOptions, Level, QRCode);
        if (Assigned(QRCode.FMatrix)) then
        begin
          SetLength(Result, QRCode.FMatrix.FHeight);
          for Y := 0 to QRCode.FMatrix.FHeight - 1 do
          begin
            SetLength(Result[Y], QRCode.FMatrix.FWidth);
            for X := 0 to QRCode.FMatrix.FWidth - 1 do
            begin
              Result[Y][X] := QRCode.FMatrix.Get(Y, X) = 1;
            end;
          end;
        end;
      finally
        QRCode.Free;
        Encoder.Free;
        Level.Free;
      end;
    end;
    
    { TDelphiZXingQRCode }
    
    constructor TDelphiZXingQRCode.Create;
    begin
      FData := '';
      FEncoding := qrAuto;
      FQuietZone := 4;
      FRows := 0;
      FColumns := 0;
    end;
    
    function TDelphiZXingQRCode.GetIsBlack(Row, Column: Integer): Boolean;
    begin
      Dec(Row, FQuietZone);
      Dec(Column, FQuietZone);
      if ((Row >= 0) and (Column >= 0) and (Row < (FRows - FQuietZone * 2)) and
        (Column < (FColumns - FQuietZone * 2))) then
      begin
        Result := FElements[Column, Row];
      end
      else
      begin
        Result := False;
      end;
    end;
    
    procedure TDelphiZXingQRCode.SetData(const NewData: string);
    begin
      if (FData <> NewData) then
      begin
        FData := NewData;
        Update;
      end;
    end;
    
    procedure TDelphiZXingQRCode.SetEncoding(NewEncoding: TQRCodeEncoding);
    begin
      if (FEncoding <> NewEncoding) then
      begin
        FEncoding := NewEncoding;
        Update;
      end;
    end;
    
    procedure TDelphiZXingQRCode.SetQuietZone(NewQuietZone: Integer);
    begin
      if ((FQuietZone <> NewQuietZone) and (NewQuietZone >= 0) and
        (NewQuietZone <= 100)) then
      begin
        FQuietZone := NewQuietZone;
        Update;
      end;
    end;
    
    procedure TDelphiZXingQRCode.Update;
    begin
      FElements := GenerateQRCode(FData, Ord(FEncoding));
      FRows := Length(FElements) + FQuietZone * 2;
      FColumns := FRows;
    end;
    
    procedure TDelphiZXingQRCode.DrawQrcode(imgQRCode: TImage;
      QRCode: TDelphiZXingQRCode);
    const
      downsizeQuality: Integer = 2;
      // bigger value, better quality, slower rendering
    var
      Row, Column: Integer;
      pixelColor: TAlphaColor;
      vBitMapData: TBitmapData;
      pixelCount, Y, X: Integer;
      columnPixel, rowPixel: Integer;
      function GetPixelCount(AWidth, AHeight: Single): Integer;
      begin
        if QRCode.Rows > 0 then
          Result := Trunc(Min(AWidth, AHeight)) div QRCode.Rows
        else
          Result := 0;
      end;
    
    begin
      pixelCount := GetPixelCount(imgQRCode.Width, imgQRCode.Height);
      imgQRCode.DisableInterpolation := False;
      if imgQRCode.WrapMode = TImageWrapMode.iwStretch then
        imgQRCode.WrapMode := TImageWrapMode.iwCenter;
      imgQRCode.DisableInterpolation := True;
    
      case imgQRCode.WrapMode of
        TImageWrapMode.iwOriginal, TImageWrapMode.iwTile, TImageWrapMode.iwCenter:
          begin
            if pixelCount > 0 then
              imgQRCode.Bitmap.SetSize(QRCode.Columns * pixelCount,
                QRCode.Rows * pixelCount);
          end;
        TImageWrapMode.iwFit:
          begin
            if pixelCount > 0 then
            begin
              imgQRCode.Bitmap.SetSize(QRCode.Columns * pixelCount *
                downsizeQuality, QRCode.Rows * pixelCount * downsizeQuality);
              pixelCount := pixelCount * downsizeQuality;
            end;
          end;
      end;
      try
        imgQRCode.Bitmap.Canvas.Clear(TAlphaColors.White);
        if pixelCount > 0 then
        begin
          if imgQRCode.Bitmap.Map(TMapAccess.maWrite, vBitMapData) then
          begin
            try
              for Row := 0 to QRCode.Rows - 1 do
              begin
                for Column := 0 to QRCode.Columns - 1 do
                begin
                  if (QRCode.IsBlack[Row, Column]) then
                    pixelColor := TAlphaColors.Black
                  else
                    pixelColor := TAlphaColors.White;
                  columnPixel := Column * pixelCount;
                  rowPixel := Row * pixelCount;
                  for X := 0 to pixelCount - 1 do
                    for Y := 0 to pixelCount - 1 do
                      vBitMapData.SetPixel(columnPixel + X, rowPixel + Y,
                        pixelColor);
                end;
              end;
            finally
              imgQRCode.Bitmap.Unmap(vBitMapData);
            end;
          end;
        end;
      finally
      end;
    end;
    
    end.
  • 相关阅读:
    InitializingBean
    线程池
    maven
    mysql主从库
    zookeeper
    分布式服务框架 Zookeeper -- 管理分布式环境中的数据
    远程调试
    enum
    注解
    Shell错误[: missing `]'
  • 原文地址:https://www.cnblogs.com/qiufeng2014/p/4281761.html
Copyright © 2011-2022 走看看