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
新的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.

http://www.cnblogs.com/qiufeng2014/p/4281761.html
Delphi xe7 FireMonkey / Mobile (Android, iOS)生成 QR Code完整实例的更多相关文章
- Delphi xe7 up1 调用android振动功能
Delphi xe7 up1 调用android振动功能 振动用到以下4个单元: Androidapi.JNI.App,Androidapi.JNIBridge,Androidapi.JNI.Os,A ...
- VS2015 VB.Net利用QrCodeNet生成QR Code
Step by step Create QR Code with QrCodeNet Step.1 新建項目 Step.2 下載QrCodeNet代碼,解壓\QrCodeNet\sourceCode\ ...
- php和jquery生成QR Code
php生产QR Code 下载qrcode源码,地址:https://sourceforge.net/projects/phpqrcode/files/releases/ 1.解压后引入qrlib.p ...
- VS2015 C#利用QrCodeNet生成QR Code
Step by step Create QR Code with QrCodeNet Step.1 新建項目 Step.2 在窗口中拖入一個Button Step.3 下載QrCodeNet代碼,解壓 ...
- 在线生成 QR Code
http://tool.oschina.net/qr 在线生成二维码(QR码)-采用ZXing与d-project
- iOS Workflow 分享 - Create QR Code
上次我分享了一个 Scan QR Code 的 Workflow,这次我分享一个正好相反的.如果我要分享一个 URL(或者是一段非常短的文本)给别人,我就可以用这个 Workflow 来生成 QR C ...
- Delphi APP 開發入門(二)Android/iOS設定,Hello World
Delphi APP 開發入門(二)Android/iOS設定,Hello World 分享: Share on facebookShare on twitterShare on google_plu ...
- [修复] Firemonkey 画线问题(Android & iOS 平台)
问题:官方 QC 的一个 Firemonkey 移动平台画线问题: RSP-14309: [iOS & Android] Delphi 10.1 Berlin - drawing proble ...
- [修复] Firemonkey 使用 DrawPath 断线问题(Android & iOS 平台)
问题:使用 Canvas.DrawPath 绘制时,最后一点无法画到终点位置.(这个问题要在粗线才能察觉) 适用:Delphi 10 Seattle (或更早的版本) for Android & ...
随机推荐
- 【分享送书】NGUI全面实践教程V3.8.2 活动开始了!!
[分享送书]NGUI全面实践教程V3.8.2 活动开始了!! 活动奖品: 活动地址:http://dwz.cn/JHdlu
- TODO:Half Half的设计
IMessageHandler :消息同步处理接口 AbsQueue:消息队列处理层,可以使用Template Method进行设计 INetWorkLayer:专门处理网络IO的,并附带多线程与异步 ...
- ural 1153. Supercomputer
1153. Supercomputer Time limit: 2.0 secondMemory limit: 64 MB To check the speed of JCN Corporation ...
- BZOJ4123 : [Baltic2015]Hacker
黑掉的一定是一个长度为$\lfloor\frac{n+1}{2}\rfloor$的区间. 于是枚举初始点,然后查询包含它的区间的最小值. 通过维护前后缀最小值+单调队列$O(n)$解决. #inclu ...
- [leetCode][012] Two Sum (1)
[题目]: Given an array of integers, find two numbers such that they add up to a specific target number ...
- [Unity2D]精灵动画
通常我们在游戏里面创建的精灵比如玩家主角,它在移动的过程中一般会带有一些动画的效果,比如两只脚前后地移动,那么这种动画效果的实现和控制就可以通过Unity2D的动画系统来实现. 要添加这样的动画,首先 ...
- 【POJ】2954 Triangle(pick定理)
http://poj.org/problem?id=2954 表示我交了20+次... 为什么呢?因为多组数据我是这样判断的:da=sum{a[i].x+a[i].y},然后!da就表示没有数据了QA ...
- 【BZOJ】1002: [FJOI2007]轮状病毒(DP+规律+高精度)
http://www.lydsy.com/JudgeOnline/problem.php?id=1002 其实我还是看题解的,而且看了题解也没明白那公式怎么来的T_T,先水过了先把....以后研究一下 ...
- 【wikioi】1285 宠物收养所
题目链接:http://www.wikioi.com/problem/1285/ 算法:Splay 刚开始看到这题,就注意到特征abs了,并且数据n<=80000显然不能暴力,只能用nlgn的做 ...
- oracle系列--第二篇 oracle下载
对于很多新手来说,包括我之前也是这样,知道oracle数据库,但是就是不知道在哪里下载.有时候,上到oracle官方网站上面都找不到下载的地方. 这不像apache里面那么直接,我们想下载如:tomc ...