From d176066cc87fb9b8237ecee70b2a10e85231cb85 Mon Sep 17 00:00:00 2001 From: Rookiestyle Date: Sun, 1 Nov 2020 15:23:26 +0100 Subject: [PATCH] Improve QR code handling Switch to ZXing.net to read / write QR images Support data-uri containing a string represenation of QR images (data:image/...) --- src/KeePassOTP.csproj | 16 +- src/KeePassOTPExt.cs | 12 +- src/KeePassOTPSetup.cs | 19 +- src/Properties/AssemblyInfo.cs | 4 +- src/QRCode/QRCode.cs | 224 --- src/QRCode/QRCodeData.cs | 205 --- src/QRCode/QRCodeDecoder.cs | 3074 -------------------------------- src/QRCode/QRCodeGenerator.cs | 1626 ----------------- version.info | 2 +- 9 files changed, 30 insertions(+), 5152 deletions(-) delete mode 100644 src/QRCode/QRCode.cs delete mode 100644 src/QRCode/QRCodeData.cs delete mode 100644 src/QRCode/QRCodeDecoder.cs delete mode 100644 src/QRCode/QRCodeGenerator.cs diff --git a/src/KeePassOTP.csproj b/src/KeePassOTP.csproj index ec527ee..eede016 100644 --- a/src/KeePassOTP.csproj +++ b/src/KeePassOTP.csproj @@ -80,11 +80,7 @@ True Resources.resx - - - - - + @@ -129,7 +125,7 @@ {10938016-dee2-4a25-9a5a-8fd3444379ca} KeePass bin\Release\protobuf-net.dll - + bin\Release\zxing.dll + bin\Release\zxing.presentation.dll + @@ -151,6 +149,10 @@ 2.4.6 + + 0.16.6 + + diff --git a/src/KeePassOTPExt.cs b/src/KeePassOTPExt.cs index 90a5234..57bd56d 100644 --- a/src/KeePassOTPExt.cs +++ b/src/KeePassOTPExt.cs @@ -228,11 +228,11 @@ private void OnOTPQRCode(object sender, EventArgs e) if (!otp.Valid) return; try { - byte[] bOTP = otp.OTPAuthString.ReadUtf8(); - QRCoder.QRCodeData qrd = QRCoder.QRCodeGenerator.GenerateQrCode(bOTP, QRCoder.QRCodeGenerator.ECCLevel.Q); - MemUtil.ZeroByteArray(bOTP); - QRCoder.QRCode qrc = new QRCoder.QRCode(qrd); - Bitmap bmp = qrc.GetGraphic(8); + ZXing.BarcodeWriter zBW = new ZXing.BarcodeWriter(); + zBW.Options.Height = 320; + zBW.Options.Width = 320; + zBW.Format = ZXing.BarcodeFormat.QR_CODE; + Bitmap bmp = zBW.Write(otp.OTPAuthString.ReadString()); QRForm f = new QRForm(); f.FormBorderStyle = FormBorderStyle.FixedDialog; f.StartPosition = FormStartPosition.CenterParent; @@ -281,8 +281,6 @@ private void OnOTPQRCode(object sender, EventArgs e) f.ShowDialog(KeePass.UI.GlobalWindowManager.TopWindow); pb.Image.Dispose(); f.Dispose(); - qrc.Dispose(); - qrd.Dispose(); } catch { } } diff --git a/src/KeePassOTPSetup.cs b/src/KeePassOTPSetup.cs index 7496046..f01fa69 100644 --- a/src/KeePassOTPSetup.cs +++ b/src/KeePassOTPSetup.cs @@ -107,6 +107,15 @@ private bool SettingsChanged() private void UpdatePreview() { if (m_NoUpdate) return; + if (StrUtil.IsDataUri(tbOTPSeed.Text)) + { + try + { + OTP.OTPAuthString = ParseFromImageByteArray(StrUtil.DataUriToData(tbOTPSeed.Text)); + InitSettings(true); + } + catch { tbOTPSeed.Text = string.Empty; } + } if (tbOTPSeed.Text.ToLowerInvariant().StartsWith("otpauth://")) { OTP.OTPAuthString = new ProtectedString(true, tbOTPSeed.Text); @@ -377,12 +386,10 @@ private ProtectedString ParseFromImageByteArray(byte[] buffer) private ProtectedString ParseFromImage(System.Drawing.Bitmap bitmap) { if (bitmap == null) return ProtectedString.EmptyEx; - QRDecoder.QRDecoder decoder = new QRDecoder.QRDecoder(); - byte[][] DataByteArray = decoder.ImageDecoder(bitmap); - if ((DataByteArray == null) || (DataByteArray.Length < 1)) return ProtectedString.EmptyEx; - ProtectedString psResult = new ProtectedString(true, DataByteArray[0]); - MemUtil.ZeroByteArray(DataByteArray[0]); - return psResult; + ZXing.BarcodeReader r = new ZXing.BarcodeReader(); + ZXing.Result result = r.Decode(bitmap); + if (result != null) return new ProtectedString(true, result.Text); + return ProtectedString.EmptyEx; } private void pbQR_DragEnter(object sender, DragEventArgs e) diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs index fe6bad6..da94669 100644 --- a/src/Properties/AssemblyInfo.cs +++ b/src/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.18.2")] -[assembly: AssemblyFileVersion("0.18.2")] \ No newline at end of file +[assembly: AssemblyVersion("0.19")] +[assembly: AssemblyFileVersion("0.19")] \ No newline at end of file diff --git a/src/QRCode/QRCode.cs b/src/QRCode/QRCode.cs deleted file mode 100644 index d10a91d..0000000 --- a/src/QRCode/QRCode.cs +++ /dev/null @@ -1,224 +0,0 @@ -/* -This file was taken from https://github.com/codebude/QRCoder and is licensed under the MIT license. ---------------------- -The MIT License (MIT) - -Copyright (c) 2013-2015 Raffael Herrmann - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -using System; -using System.Drawing; -using System.Drawing.Drawing2D; - -namespace QRCoder -{ - public class QRCode : AbstractQRCode, IDisposable - { - /// - /// Constructor without params to be used in COM Objects connections - /// - public QRCode() { } - - public QRCode(QRCodeData data) : base(data) { } - - public Bitmap GetGraphic(int pixelsPerModule) - { - return this.GetGraphic(pixelsPerModule, Color.Black, Color.White, true); - } - - public Bitmap GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true) - { - return this.GetGraphic(pixelsPerModule, ColorTranslator.FromHtml(darkColorHtmlHex), ColorTranslator.FromHtml(lightColorHtmlHex), drawQuietZones); - } - - public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true) - { - var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; - var offset = drawQuietZones ? 0 : 4 * pixelsPerModule; - - var bmp = new Bitmap(size, size); - using (var gfx = Graphics.FromImage(bmp)) - using (var lightBrush = new SolidBrush(lightColor)) - using (var darkBrush = new SolidBrush(darkColor)) - { - for (var x = 0; x < size + offset; x = x + pixelsPerModule) - { - for (var y = 0; y < size + offset; y = y + pixelsPerModule) - { - var module = this.QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1]; - - if (module) - { - gfx.FillRectangle(darkBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); - } - else - { - gfx.FillRectangle(lightBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); - } - } - } - - gfx.Save(); - } - - return bmp; - } - - public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap icon = null, int iconSizePercent = 15, int iconBorderWidth = 6, bool drawQuietZones = true) - { - var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; - var offset = drawQuietZones ? 0 : 4 * pixelsPerModule; - - var bmp = new Bitmap(size, size, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - - using (var gfx = Graphics.FromImage(bmp)) - using (var lightBrush = new SolidBrush(lightColor)) - using (var darkBrush = new SolidBrush(darkColor)) - { - gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; - gfx.CompositingQuality = CompositingQuality.HighQuality; - gfx.Clear(lightColor); - - var drawIconFlag = icon != null && iconSizePercent > 0 && iconSizePercent <= 100; - - GraphicsPath iconPath = null; - float iconDestWidth = 0, iconDestHeight = 0, iconX = 0, iconY = 0; - - if (drawIconFlag) - { - iconDestWidth = iconSizePercent * bmp.Width / 100f; - iconDestHeight = drawIconFlag ? iconDestWidth * icon.Height / icon.Width : 0; - iconX = (bmp.Width - iconDestWidth) / 2; - iconY = (bmp.Height - iconDestHeight) / 2; - - var centerDest = new RectangleF(iconX - iconBorderWidth, iconY - iconBorderWidth, iconDestWidth + iconBorderWidth * 2, iconDestHeight + iconBorderWidth * 2); - iconPath = this.CreateRoundedRectanglePath(centerDest, iconBorderWidth * 2); - } - - for (var x = 0; x < size + offset; x = x + pixelsPerModule) - { - for (var y = 0; y < size + offset; y = y + pixelsPerModule) - { - var module = this.QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1]; - - if (module) - { - var r = new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule); - - if (drawIconFlag) - { - var region = new Region(r); - region.Exclude(iconPath); - gfx.FillRegion(darkBrush, region); - } - else - { - gfx.FillRectangle(darkBrush, r); - } - } - else - { - gfx.FillRectangle(lightBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); - } - } - } - - if (drawIconFlag) - { - var iconDestRect = new RectangleF(iconX, iconY, iconDestWidth, iconDestHeight); - gfx.DrawImage(icon, iconDestRect, new RectangleF(0, 0, icon.Width, icon.Height), GraphicsUnit.Pixel); - } - - gfx.Save(); - } - - return bmp; - } - - internal GraphicsPath CreateRoundedRectanglePath(RectangleF rect, int cornerRadius) - { - var roundedRect = new GraphicsPath(); - roundedRect.AddArc(rect.X, rect.Y, cornerRadius * 2, cornerRadius * 2, 180, 90); - roundedRect.AddLine(rect.X + cornerRadius, rect.Y, rect.Right - cornerRadius * 2, rect.Y); - roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y, cornerRadius * 2, cornerRadius * 2, 270, 90); - roundedRect.AddLine(rect.Right, rect.Y + cornerRadius * 2, rect.Right, rect.Y + rect.Height - cornerRadius * 2); - roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y + rect.Height - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 0, 90); - roundedRect.AddLine(rect.Right - cornerRadius * 2, rect.Bottom, rect.X + cornerRadius * 2, rect.Bottom); - roundedRect.AddArc(rect.X, rect.Bottom - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 90, 90); - roundedRect.AddLine(rect.X, rect.Bottom - cornerRadius * 2, rect.X, rect.Y + cornerRadius * 2); - roundedRect.CloseFigure(); - return roundedRect; - } - } - - public static class QRCodeHelper - { - public static Bitmap GetQRCode(string plainText, int pixelsPerModule, Color darkColor, Color lightColor, QRCodeGenerator.ECCLevel eccLevel) - { - Bitmap icon = null; - int iconSizePercent = 15; - int iconBorderWidth = 6; - bool drawQuietZones = true; - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel)) - using (var qrCode = new QRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColor, lightColor, icon, iconSizePercent, iconBorderWidth, drawQuietZones); - } - } - - public abstract class AbstractQRCode - { - protected QRCodeData QrCodeData { get; set; } - - protected AbstractQRCode() - { - } - - protected AbstractQRCode(QRCodeData data) - { - this.QrCodeData = data; - } - - /// - /// Set a QRCodeData object that will be used to generate QR code. Used in COM Objects connections - /// - /// Need a QRCodeData object generated by QRCodeGenerator.CreateQrCode() - virtual public void SetQRCodeData(QRCodeData data) - { - this.QrCodeData = data; - } - - public void Dispose() - { - if (this.QrCodeData != null) this.QrCodeData.Dispose(); - this.QrCodeData = null; - } - } -} - -namespace QRCoder.Exceptions -{ - public class DataTooLongException : Exception - { - public DataTooLongException(string eccLevel, string encodingMode, int maxSizeByte) : base( - "The given payload exceeds the maximum size of the QR code standard. The maximum size allowed for the choosen paramters (ECC level={eccLevel}, EncodingMode={encodingMode}) is {maxSizeByte} byte." - ) - { } - } -} \ No newline at end of file diff --git a/src/QRCode/QRCodeData.cs b/src/QRCode/QRCodeData.cs deleted file mode 100644 index 529f640..0000000 --- a/src/QRCode/QRCodeData.cs +++ /dev/null @@ -1,205 +0,0 @@ -/* -This file was taken from https://github.com/codebude/QRCoder and is licensed under the MIT license. ---------------------- -The MIT License (MIT) - -Copyright (c) 2013-2015 Raffael Herrmann - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -namespace QRCoder -{ - //using QRCoder.Framework4._0Methods; - using System; - using System.IO; - using System.IO.Compression; - - public class QRCodeData : IDisposable - { - public List ModuleMatrix { get; set; } - - public QRCodeData(int version) - { - this.Version = version; - var size = ModulesPerSideFromVersion(version); - this.ModuleMatrix = new List(); - for (var i = 0; i < size; i++) - this.ModuleMatrix.Add(new BitArray(size)); - } - public QRCodeData(string pathToRawData, Compression compressMode) : this(File.ReadAllBytes(pathToRawData), compressMode) - { - } - public QRCodeData(byte[] rawData, Compression compressMode) - { - var bytes = new List(rawData); - - //Decompress - if (compressMode == Compression.Deflate) - { - using (var input = new MemoryStream(bytes.ToArray())) - { - using (var output = new MemoryStream()) - { - using (var dstream = new DeflateStream(input, CompressionMode.Decompress)) - { - //Stream4Methods.CopyTo(dstream, output); - } - bytes = new List(output.ToArray()); - } - } - } - else if (compressMode == Compression.GZip) - { - using (var input = new MemoryStream(bytes.ToArray())) - { - using (var output = new MemoryStream()) - { - using (var dstream = new GZipStream(input, CompressionMode.Decompress)) - { - //Stream4Methods.CopyTo(dstream, output); - } - bytes = new List(output.ToArray()); - } - } - } - - if (bytes[0] != 0x51 || bytes[1] != 0x52 || bytes[2] != 0x52) - throw new Exception("Invalid raw data file. Filetype doesn't match \"QRR\"."); - - //Set QR code version - var sideLen = (int)bytes[4]; - bytes.RemoveRange(0, 5); - this.Version = (sideLen - 21 - 8) / 4 + 1; - - //Unpack - var modules = new Queue(8 * bytes.Count); - foreach (var b in bytes) - { - var bArr = new BitArray(new byte[] { b }); - for (int i = 7; i >= 0; i--) - { - modules.Enqueue((b & (1 << i)) != 0); - } - } - - //Build module matrix - this.ModuleMatrix = new List(sideLen); - for (int y = 0; y < sideLen; y++) - { - this.ModuleMatrix.Add(new BitArray(sideLen)); - for (int x = 0; x < sideLen; x++) - { - this.ModuleMatrix[y][x] = modules.Dequeue(); - } - } - - } - - public byte[] GetRawData(Compression compressMode) - { - var bytes = new List(); - - //Add header - signature ("QRR") - bytes.AddRange(new byte[] { 0x51, 0x52, 0x52, 0x00 }); - - //Add header - rowsize - bytes.Add((byte)ModuleMatrix.Count); - - //Build data queue - var dataQueue = new Queue(); - foreach (var row in ModuleMatrix) - { - foreach (var module in row) - { - dataQueue.Enqueue((bool)module ? 1 : 0); - } - } - for (int i = 0; i < 8 - (ModuleMatrix.Count * ModuleMatrix.Count) % 8; i++) - { - dataQueue.Enqueue(0); - } - - //Process queue - while (dataQueue.Count > 0) - { - byte b = 0; - for (int i = 7; i >= 0; i--) - { - b += (byte)(dataQueue.Dequeue() << i); - } - bytes.Add(b); - } - var rawData = bytes.ToArray(); - - //Compress stream (optional) - if (compressMode == Compression.Deflate) - { - using (var output = new MemoryStream()) - { - using (var dstream = new DeflateStream(output, CompressionMode.Compress)) - { - dstream.Write(rawData, 0, rawData.Length); - } - rawData = output.ToArray(); - } - } - else if (compressMode == Compression.GZip) - { - using (var output = new MemoryStream()) - { - using (GZipStream gzipStream = new GZipStream(output, CompressionMode.Compress, true)) - { - gzipStream.Write(rawData, 0, rawData.Length); - } - rawData = output.ToArray(); - } - } - return rawData; - } - - public void SaveRawData(string filePath, Compression compressMode) - { - File.WriteAllBytes(filePath, GetRawData(compressMode)); - } - - public int Version { get; private set; } - - private static int ModulesPerSideFromVersion(int version) - { - return 21 + (version - 1) * 4; - } - - public void Dispose() - { - this.ModuleMatrix = null; - this.Version = 0; - - } - - public enum Compression - { - Uncompressed, - Deflate, - GZip - } - } -} \ No newline at end of file diff --git a/src/QRCode/QRCodeDecoder.cs b/src/QRCode/QRCodeDecoder.cs deleted file mode 100644 index 1992435..0000000 --- a/src/QRCode/QRCodeDecoder.cs +++ /dev/null @@ -1,3074 +0,0 @@ -// -// QR Code Library -// -// QR Code decoder. -// -// Author: Uzi Granot -// Version: 1.0 -// Date: June 30, 2018 -// Copyright (C) 2013-2018 Uzi Granot. All Rights Reserved -// -// QR Code Library C# class library and the attached test/demo -// applications are free software. -// Software developed by this author is licensed under CPOL 1.02. -// Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser -// General Public License v3.0. -// -// The solution is made of 4 projects: -// 1. QRCodeEncoderDecoderLibrary: QR code encoding and decoding. -// 2. QRCodeEncoderDemo: Create QR Code images. -// 3. QRCodeDecoderDemo: Decode QR code image files. -// 4. QRCodeVideoDecoder: Decode QR code using web camera. -// This demo program is using some of the source modules of -// Camera_Net project published at CodeProject.com: -// https://www.codeproject.com/Articles/671407/Camera_Net-Library -// and at GitHub: https://github.com/free5lot/Camera_Net. -// This project is based on DirectShowLib. -// http://sourceforge.net/projects/directshownet/ -// This project includes a modified subset of the source modules. -// -// The main points of CPOL 1.02 subject to the terms of the License are: -// -// Source Code and Executable Files can be used in commercial applications; -// Source Code and Executable Files can be redistributed; and -// Source Code can be modified to create derivative works. -// No claim of suitability, guarantee, or any warranty whatsoever is -// provided. The software is provided "as-is". -// The Article accompanying the Work may not be distributed or republished -// without the Author's consent -// -// For version history please refer to QRCode.cs -///////////////////////////////////////////////////////////////////// -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.Text; -using System.Runtime.InteropServices; - -namespace QRDecoder -{ - public class QRDecoder - { - public const string VersionNumber = "Rev 2.0.0 - 2019-05-15"; - - /// - /// Gets QR Code matrix version - /// - public int QRCodeVersion { get; internal set; } - - /// - /// Gets QR Code matrix dimension in bits - /// - public int QRCodeDimension { get; internal set; } - - /// - /// Gets QR Code error correction code (L, M, Q, H) - /// - public ErrorCorrection ErrorCorrection { get; internal set; } - - /// - /// Error correction percent (L, M, Q, H) - /// - public int[] ErrCorrPercent = new int[] { 7, 15, 25, 30 }; - - /// - /// Get mask code (0 to 7) - /// - public int MaskCode { get; internal set; } - - internal int ImageWidth; - internal int ImageHeight; - internal bool[,] BlackWhiteImage; - internal List FinderList; - internal List AlignList; - internal List DataArrayList; - internal int MaxCodewords; - internal int MaxDataCodewords; - internal int MaxDataBits; - internal int ErrCorrCodewords; - internal int BlocksGroup1; - internal int DataCodewordsGroup1; - internal int BlocksGroup2; - internal int DataCodewordsGroup2; - - internal byte[] CodewordsArray; - internal int CodewordsPtr; - internal uint BitBuffer; - internal int BitBufferLen; - internal byte[,] BaseMatrix; - internal byte[,] MaskMatrix; - - internal bool Trans4Mode; - - // transformation cooefficients from QR modules to image pixels - internal double Trans3a; - internal double Trans3b; - internal double Trans3c; - internal double Trans3d; - internal double Trans3e; - internal double Trans3f; - - // transformation matrix based on three finders plus one more point - internal double Trans4a; - internal double Trans4b; - internal double Trans4c; - internal double Trans4d; - internal double Trans4e; - internal double Trans4f; - internal double Trans4g; - internal double Trans4h; - - internal const double SIGNATURE_MAX_DEVIATION = 0.25; - internal const double HOR_VERT_SCAN_MAX_DISTANCE = 2.0; - internal const double MODULE_SIZE_DEVIATION = 0.5; // 0.75; - internal const double CORNER_SIDE_LENGTH_DEV = 0.8; - internal const double CORNER_RIGHT_ANGLE_DEV = 0.25; // about Sin(4 deg) - internal const double ALIGNMENT_SEARCH_AREA = 0.3; - - /* - public static string ByteArrayToStr(byte[] DataArray) - { - Decoder Decoder = Encoding.UTF8.GetDecoder(); - int CharCount = Decoder.GetCharCount(DataArray, 0, DataArray.Length); - char[] CharArray = new char[CharCount]; - Decoder.GetChars(DataArray, 0, DataArray.Length, CharArray, 0); - return new string(CharArray); - } - */ - - //////////////////////////////////////////////////////////////////// - // Decode QRCode boolean matrix - //////////////////////////////////////////////////////////////////// - public byte[][] ImageDecoder(Bitmap InputImage) - { - try - { - // empty data string output - DataArrayList = new List(); - - // save image dimension - ImageWidth = InputImage.Width; - ImageHeight = InputImage.Height; - - // convert input image to black and white boolean image - if (!ConvertImageToBlackAndWhite(InputImage)) return null; - - // horizontal search for finders - if (!HorizontalFindersSearch()) return null; - - // vertical search for finders - VerticalFindersSearch(); - - // remove unused finders - if (!RemoveUnusedFinders()) return null; - } - catch { return null; } - - // look for all possible 3 finder patterns - int Index1End = FinderList.Count - 2; - int Index2End = FinderList.Count - 1; - int Index3End = FinderList.Count; - for (int Index1 = 0; Index1 < Index1End; Index1++) - { - for (int Index2 = Index1 + 1; Index2 < Index2End; Index2++) - { - for (int Index3 = Index2 + 1; Index3 < Index3End; Index3++) - { - try - { - // find 3 finders arranged in L shape - Corner Corner = Corner.CreateCorner(FinderList[Index1], FinderList[Index2], FinderList[Index3]); - - // not a valid corner - if (Corner == null) continue; - - // get corner info (version, error code and mask) - // continue if failed - if (!GetQRCodeCornerInfo(Corner)) continue; - - // decode corner using three finders - // continue if successful - if (DecodeQRCodeCorner(Corner)) continue; - - // qr code version 1 has no alignment mark - // in other words decode failed - if (QRCodeVersion == 1) continue; - - // find bottom right alignment mark - // continue if failed - if (!FindAlignmentMark(Corner)) continue; - - // decode using 4 points - foreach (Finder Align in AlignList) - { - // calculate transformation based on 3 finders and bottom right alignment mark - SetTransMatrix(Corner, Align.Row, Align.Col); - - // decode corner using three finders and one alignment mark - if (DecodeQRCodeCorner(Corner)) break; - } - } - catch { continue; } - } - } - } - - // not found exit - if (DataArrayList.Count == 0) return null; - - // successful exit - return DataArrayList.ToArray(); - } - - //////////////////////////////////////////////////////////////////// - // Convert image to black and white boolean matrix - //////////////////////////////////////////////////////////////////// - private bool ConvertImageToBlackAndWhite(Bitmap InputImage) - { - // lock image bits - BitmapData BitmapData = InputImage.LockBits(new Rectangle(0, 0, ImageWidth, ImageHeight), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); - - // address of first line - IntPtr BitArrayPtr = BitmapData.Scan0; - - // length in bytes of one scan line - int ScanLineWidth = BitmapData.Stride; - if (ScanLineWidth < 0) return false; - - // image total bytes - int TotalBytes = ScanLineWidth * ImageHeight; - byte[] BitmapArray = new byte[TotalBytes]; - - // Copy the RGB values into the array. - Marshal.Copy(BitArrayPtr, BitmapArray, 0, TotalBytes); - - // unlock image - InputImage.UnlockBits(BitmapData); - - // allocate gray image - byte[,] GrayImage = new byte[ImageHeight, ImageWidth]; - int[] GrayLevel = new int[256]; - - // convert to gray - int Delta = ScanLineWidth - 3 * ImageWidth; - int BitmapPtr = 0; - for (int Row = 0; Row < ImageHeight; Row++) - { - for (int Col = 0; Col < ImageWidth; Col++) - { - int Module = (30 * BitmapArray[BitmapPtr] + 59 * BitmapArray[BitmapPtr + 1] + 11 * BitmapArray[BitmapPtr + 2]) / 100; - GrayLevel[Module]++; - GrayImage[Row, Col] = (byte)Module; - BitmapPtr += 3; - } - BitmapPtr += Delta; - } - - // gray level cutoff between black and white - int LevelStart; - int LevelEnd; - for (LevelStart = 0; LevelStart < 256 && GrayLevel[LevelStart] == 0; LevelStart++) ; - for (LevelEnd = 255; LevelEnd >= LevelStart && GrayLevel[LevelEnd] == 0; LevelEnd--) ; - LevelEnd++; - if (LevelEnd - LevelStart < 2) return false; - - int CutoffLevel = (LevelStart + LevelEnd) / 2; - - // create boolean image white = false, black = true - BlackWhiteImage = new bool[ImageHeight, ImageWidth]; - for (int Row = 0; Row < ImageHeight; Row++) - { - for (int Col = 0; Col < ImageWidth; Col++) - { - BlackWhiteImage[Row, Col] = GrayImage[Row, Col] < CutoffLevel; - } - } - return true; - } - - //////////////////////////////////////////////////////////////////// - // search row by row for finders blocks - //////////////////////////////////////////////////////////////////// - internal bool HorizontalFindersSearch() - { - // create empty finders list - FinderList = new List(); - - // look for finder patterns - int[] ColPos = new int[ImageWidth + 1]; - int PosPtr = 0; - - // scan one row at a time - for (int Row = 0; Row < ImageHeight; Row++) - { - // look for first black pixel - int Col; - for (Col = 0; Col < ImageWidth && !BlackWhiteImage[Row, Col]; Col++) ; - if (Col == ImageWidth) continue; - - // first black - PosPtr = 0; - ColPos[PosPtr++] = Col; - - // loop for pairs - for (; ; ) - { - // look for next white - // if black is all the way to the edge, set next white after the edge - for (; Col < ImageWidth && BlackWhiteImage[Row, Col]; Col++) ; - ColPos[PosPtr++] = Col; - if (Col == ImageWidth) break; - - // look for next black - for (; Col < ImageWidth && !BlackWhiteImage[Row, Col]; Col++) ; - if (Col == ImageWidth) break; - ColPos[PosPtr++] = Col; - } - - // we must have at least 6 positions - if (PosPtr < 6) continue; - - // build length array - int PosLen = PosPtr - 1; - int[] Len = new int[PosLen]; - for (int Ptr = 0; Ptr < PosLen; Ptr++) Len[Ptr] = ColPos[Ptr + 1] - ColPos[Ptr]; - - // test signature - int SigLen = PosPtr - 5; - for (int SigPtr = 0; SigPtr < SigLen; SigPtr += 2) - { - double ModuleSize; - if (TestFinderSig(ColPos, Len, SigPtr, out ModuleSize)) - FinderList.Add(new Finder(Row, ColPos[SigPtr + 2], ColPos[SigPtr + 3], ModuleSize)); - } - } - - // no finders found - if (FinderList.Count < 3) return false; - - return true; - } - - //////////////////////////////////////////////////////////////////// - // search row by row for alignment blocks - //////////////////////////////////////////////////////////////////// - - internal bool HorizontalAlignmentSearch(int AreaLeft, int AreaTop, int AreaWidth, int AreaHeight) - { - // create empty finders list - AlignList = new List(); - - // look for finder patterns - int[] ColPos = new int[AreaWidth + 1]; - int PosPtr = 0; - - // area right and bottom - int AreaRight = AreaLeft + AreaWidth; - int AreaBottom = AreaTop + AreaHeight; - - // scan one row at a time - for (int Row = AreaTop; Row < AreaBottom; Row++) - { - // look for first black pixel - int Col; - for (Col = AreaLeft; Col < AreaRight && !BlackWhiteImage[Row, Col]; Col++) ; - if (Col == AreaRight) continue; - - // first black - PosPtr = 0; - ColPos[PosPtr++] = Col; - - // loop for pairs - for (; ; ) - { - // look for next white - // if black is all the way to the edge, set next white after the edge - for (; Col < AreaRight && BlackWhiteImage[Row, Col]; Col++) ; - ColPos[PosPtr++] = Col; - if (Col == AreaRight) break; - - // look for next black - for (; Col < AreaRight && !BlackWhiteImage[Row, Col]; Col++) ; - if (Col == AreaRight) break; - ColPos[PosPtr++] = Col; - } - - // we must have at least 6 positions - if (PosPtr < 6) continue; - - // build length array - int PosLen = PosPtr - 1; - int[] Len = new int[PosLen]; - for (int Ptr = 0; Ptr < PosLen; Ptr++) Len[Ptr] = ColPos[Ptr + 1] - ColPos[Ptr]; - - // test signature - int SigLen = PosPtr - 5; - for (int SigPtr = 0; SigPtr < SigLen; SigPtr += 2) - { - double ModuleSize; - if (TestAlignSig(ColPos, Len, SigPtr, out ModuleSize)) - AlignList.Add(new Finder(Row, ColPos[SigPtr + 2], ColPos[SigPtr + 3], ModuleSize)); - } - } - - // list is now empty or has less than three finders - return AlignList.Count != 0; - } - - //////////////////////////////////////////////////////////////////// - // search column by column for finders blocks - //////////////////////////////////////////////////////////////////// - internal void VerticalFindersSearch() - { - // active columns - bool[] ActiveColumn = new bool[ImageWidth]; - foreach (Finder HF in FinderList) - { - for (int Col = HF.Col1; Col < HF.Col2; Col++) ActiveColumn[Col] = true; - } - - // look for finder patterns - int[] RowPos = new int[ImageHeight + 1]; - int PosPtr = 0; - - // scan one column at a time - for (int Col = 0; Col < ImageWidth; Col++) - { - // not active column - if (!ActiveColumn[Col]) continue; - - // look for first black pixel - int Row; - for (Row = 0; Row < ImageHeight && !BlackWhiteImage[Row, Col]; Row++) ; - if (Row == ImageWidth) continue; - - // first black - PosPtr = 0; - RowPos[PosPtr++] = Row; - - // loop for pairs - for (; ; ) - { - // look for next white - // if black is all the way to the edge, set next white after the edge - for (; Row < ImageHeight && BlackWhiteImage[Row, Col]; Row++) ; - RowPos[PosPtr++] = Row; - if (Row == ImageHeight) break; - - // look for next black - for (; Row < ImageHeight && !BlackWhiteImage[Row, Col]; Row++) ; - if (Row == ImageHeight) break; - RowPos[PosPtr++] = Row; - } - - // we must have at least 6 positions - if (PosPtr < 6) continue; - - // build length array - int PosLen = PosPtr - 1; - int[] Len = new int[PosLen]; - for (int Ptr = 0; Ptr < PosLen; Ptr++) Len[Ptr] = RowPos[Ptr + 1] - RowPos[Ptr]; - - // test signature - int SigLen = PosPtr - 5; - for (int SigPtr = 0; SigPtr < SigLen; SigPtr += 2) - { - double ModuleSize; - if (!TestFinderSig(RowPos, Len, SigPtr, out ModuleSize)) continue; - foreach (Finder HF in FinderList) - { - HF.Match(Col, RowPos[SigPtr + 2], RowPos[SigPtr + 3], ModuleSize); - } - } - } - } - - //////////////////////////////////////////////////////////////////// - // search column by column for finders blocks - //////////////////////////////////////////////////////////////////// - internal void VerticalAlignmentSearch(int AreaLeft, int AreaTop, int AreaWidth, int AreaHeight) - { - // active columns - bool[] ActiveColumn = new bool[AreaWidth]; - foreach (Finder HF in AlignList) - { - for (int Col = HF.Col1; Col < HF.Col2; Col++) ActiveColumn[Col - AreaLeft] = true; - } - - // look for finder patterns - int[] RowPos = new int[AreaHeight + 1]; - int PosPtr = 0; - - // area right and bottom - int AreaRight = AreaLeft + AreaWidth; - int AreaBottom = AreaTop + AreaHeight; - - // scan one column at a time - for (int Col = AreaLeft; Col < AreaRight; Col++) - { - // not active column - if (!ActiveColumn[Col - AreaLeft]) continue; - - // look for first black pixel - int Row; - for (Row = AreaTop; Row < AreaBottom && !BlackWhiteImage[Row, Col]; Row++) ; - if (Row == AreaBottom) continue; - - // first black - PosPtr = 0; - RowPos[PosPtr++] = Row; - - // loop for pairs - for (; ; ) - { - // look for next white - // if black is all the way to the edge, set next white after the edge - for (; Row < AreaBottom && BlackWhiteImage[Row, Col]; Row++) ; - RowPos[PosPtr++] = Row; - if (Row == AreaBottom) break; - - // look for next black - for (; Row < AreaBottom && !BlackWhiteImage[Row, Col]; Row++) ; - if (Row == AreaBottom) break; - RowPos[PosPtr++] = Row; - } - - // we must have at least 6 positions - if (PosPtr < 6) continue; - - // build length array - int PosLen = PosPtr - 1; - int[] Len = new int[PosLen]; - for (int Ptr = 0; Ptr < PosLen; Ptr++) Len[Ptr] = RowPos[Ptr + 1] - RowPos[Ptr]; - - // test signature - int SigLen = PosPtr - 5; - for (int SigPtr = 0; SigPtr < SigLen; SigPtr += 2) - { - double ModuleSize; - if (!TestAlignSig(RowPos, Len, SigPtr, out ModuleSize)) continue; - foreach (Finder HF in AlignList) - { - HF.Match(Col, RowPos[SigPtr + 2], RowPos[SigPtr + 3], ModuleSize); - } - } - } - } - - //////////////////////////////////////////////////////////////////// - // search column by column for finders blocks - //////////////////////////////////////////////////////////////////// - internal bool RemoveUnusedFinders() - { - // remove all entries without a match - for (int Index = 0; Index < FinderList.Count; Index++) - { - if (FinderList[Index].Distance == double.MaxValue) - { - FinderList.RemoveAt(Index); - Index--; - } - } - - // list is now empty or has less than three finders - if (FinderList.Count < 3) return false; - - // keep best entry for each overlapping area - for (int Index = 0; Index < FinderList.Count; Index++) - { - Finder Finder = FinderList[Index]; - for (int Index1 = Index + 1; Index1 < FinderList.Count; Index1++) - { - Finder Finder1 = FinderList[Index1]; - if (!Finder.Overlap(Finder1)) continue; - if (Finder1.Distance < Finder.Distance) - { - Finder = Finder1; - FinderList[Index] = Finder; - } - FinderList.RemoveAt(Index1); - Index1--; - } - } - - // list is now empty or has less than three finders - if (FinderList.Count < 3) return false; - - return true; - } - - //////////////////////////////////////////////////////////////////// - // search column by column for finders blocks - //////////////////////////////////////////////////////////////////// - internal bool RemoveUnusedAlignMarks() - { - // remove all entries without a match - for (int Index = 0; Index < AlignList.Count; Index++) - { - if (AlignList[Index].Distance == double.MaxValue) - { - AlignList.RemoveAt(Index); - Index--; - } - } - - // keep best entry for each overlapping area - for (int Index = 0; Index < AlignList.Count; Index++) - { - Finder Finder = AlignList[Index]; - for (int Index1 = Index + 1; Index1 < AlignList.Count; Index1++) - { - Finder Finder1 = AlignList[Index1]; - if (!Finder.Overlap(Finder1)) continue; - if (Finder1.Distance < Finder.Distance) - { - Finder = Finder1; - AlignList[Index] = Finder; - } - AlignList.RemoveAt(Index1); - Index1--; - } - } - - // list is now empty or has less than three finders - return AlignList.Count != 0; - } - - //////////////////////////////////////////////////////////////////// - // test finder signature 1 1 3 1 1 - //////////////////////////////////////////////////////////////////// - internal bool TestFinderSig(int[] Pos, int[] Len, int Index, out double Module) - { - Module = (Pos[Index + 5] - Pos[Index]) / 7.0; - double MaxDev = SIGNATURE_MAX_DEVIATION * Module; - if (Math.Abs(Len[Index] - Module) > MaxDev) return false; - if (Math.Abs(Len[Index + 1] - Module) > MaxDev) return false; - if (Math.Abs(Len[Index + 2] - 3 * Module) > MaxDev) return false; - if (Math.Abs(Len[Index + 3] - Module) > MaxDev) return false; - if (Math.Abs(Len[Index + 4] - Module) > MaxDev) return false; - return true; - } - - //////////////////////////////////////////////////////////////////// - // test alignment signature n 1 1 1 n - //////////////////////////////////////////////////////////////////// - - internal bool TestAlignSig(int[] Pos, int[] Len, int Index, out double Module) - { - Module = (Pos[Index + 4] - Pos[Index + 1]) / 3.0; - double MaxDev = SIGNATURE_MAX_DEVIATION * Module; - if (Len[Index] < Module - MaxDev) return false; - if (Math.Abs(Len[Index + 1] - Module) > MaxDev) return false; - if (Math.Abs(Len[Index + 2] - Module) > MaxDev) return false; - if (Math.Abs(Len[Index + 3] - Module) > MaxDev) return false; - if (Len[Index + 4] < Module - MaxDev) return false; - return true; - } - - //////////////////////////////////////////////////////////////////// - // Build corner list - //////////////////////////////////////////////////////////////////// - internal List BuildCornerList() - { - // empty list - List Corners = new List(); - - // look for all possible 3 finder patterns - int Index1End = FinderList.Count - 2; - int Index2End = FinderList.Count - 1; - int Index3End = FinderList.Count; - for (int Index1 = 0; Index1 < Index1End; Index1++) - { - for (int Index2 = Index1 + 1; Index2 < Index2End; Index2++) - { - for (int Index3 = Index2 + 1; Index3 < Index3End; Index3++) - { - // find 3 finders arranged in L shape - Corner Corner = Corner.CreateCorner(FinderList[Index1], FinderList[Index2], FinderList[Index3]); - - // add corner to list - if (Corner != null) Corners.Add(Corner); - } - } - } - return Corners.Count == 0 ? null : Corners; - } - - //////////////////////////////////////////////////////////////////// - // Get QR Code corner info - //////////////////////////////////////////////////////////////////// - internal bool GetQRCodeCornerInfo(Corner Corner) - { - try - { - // initial version number - QRCodeVersion = Corner.InitialVersionNumber(); - - // qr code dimension - QRCodeDimension = 17 + 4 * QRCodeVersion; - - // set transformation matrix - SetTransMatrix(Corner); - - // if version number is 7 or more, get version code - if (QRCodeVersion >= 7) - { - int Version = GetVersionOne(); - if (Version == 0) - { - Version = GetVersionTwo(); - if (Version == 0) return false; - } - - // QR Code version number is different than initial version - if (Version != QRCodeVersion) - { - // initial version number and dimension - QRCodeVersion = Version; - - // qr code dimension - QRCodeDimension = 17 + 4 * QRCodeVersion; - - // set transformation matrix - SetTransMatrix(Corner); - } - } - - // get format info arrays - int FormatInfo = GetFormatInfoOne(); - if (FormatInfo < 0) - { - FormatInfo = GetFormatInfoTwo(); - if (FormatInfo < 0) return false; - } - - // set error correction code and mask code - ErrorCorrection = FormatInfoToErrCode(FormatInfo >> 3); - MaskCode = FormatInfo & 7; - - // successful exit - return true; - } - catch { return false; } - } - - //////////////////////////////////////////////////////////////////// - // Search for QR Code version - //////////////////////////////////////////////////////////////////// - internal bool DecodeQRCodeCorner(Corner Corner) - { - try - { - // create base matrix - BuildBaseMatrix(); - - // create data matrix and test fixed modules - ConvertImageToMatrix(); - - // based on version and format information - // set number of data and error correction codewords length - SetDataCodewordsLength(); - - // apply mask as per get format information step - ApplyMask(MaskCode); - - // unload data from binary matrix to byte format - UnloadDataFromMatrix(); - - // restore blocks (undo interleave) - RestoreBlocks(); - - // calculate error correction - // in case of error try to correct it - CalculateErrorCorrection(); - - // decode data - byte[] DataArray = DecodeData(); - DataArrayList.Add(DataArray); - - - // successful exit - return true; - } - catch { return false; } - } - - internal void SetTransMatrix(Corner Corner) - { - // save - int BottomRightPos = QRCodeDimension - 4; - - // transformation matrix based on three finders - double[,] Matrix1 = new double[3, 4]; - double[,] Matrix2 = new double[3, 4]; - - // build matrix 1 for horizontal X direction - Matrix1[0, 0] = 3; - Matrix1[0, 1] = 3; - Matrix1[0, 2] = 1; - Matrix1[0, 3] = Corner.TopLeftFinder.Col; - - Matrix1[1, 0] = BottomRightPos; - Matrix1[1, 1] = 3; - Matrix1[1, 2] = 1; - Matrix1[1, 3] = Corner.TopRightFinder.Col; - - Matrix1[2, 0] = 3; - Matrix1[2, 1] = BottomRightPos; - Matrix1[2, 2] = 1; - Matrix1[2, 3] = Corner.BottomLeftFinder.Col; - - // build matrix 2 for Vertical Y direction - Matrix2[0, 0] = 3; - Matrix2[0, 1] = 3; - Matrix2[0, 2] = 1; - Matrix2[0, 3] = Corner.TopLeftFinder.Row; - - Matrix2[1, 0] = BottomRightPos; - Matrix2[1, 1] = 3; - Matrix2[1, 2] = 1; - Matrix2[1, 3] = Corner.TopRightFinder.Row; - - Matrix2[2, 0] = 3; - Matrix2[2, 1] = BottomRightPos; - Matrix2[2, 2] = 1; - Matrix2[2, 3] = Corner.BottomLeftFinder.Row; - - // solve matrix1 - SolveMatrixOne(Matrix1); - Trans3a = Matrix1[0, 3]; - Trans3c = Matrix1[1, 3]; - Trans3e = Matrix1[2, 3]; - - // solve matrix2 - SolveMatrixOne(Matrix2); - Trans3b = Matrix2[0, 3]; - Trans3d = Matrix2[1, 3]; - Trans3f = Matrix2[2, 3]; - - // reset trans 4 mode - Trans4Mode = false; - } - - internal void SolveMatrixOne(double[,] Matrix) - { - for (int Row = 0; Row < 3; Row++) - { - // If the element is zero, make it non zero by adding another row - if (Matrix[Row, Row] == 0) - { - int Row1; - for (Row1 = Row + 1; Row1 < 3 && Matrix[Row1, Row] == 0; Row1++) ; - if (Row1 == 3) throw new ApplicationException("Solve linear equations failed"); - - for (int Col = Row; Col < 4; Col++) Matrix[Row, Col] += Matrix[Row1, Col]; - } - - // make the diagonal element 1.0 - for (int Col = 3; Col > Row; Col--) Matrix[Row, Col] /= Matrix[Row, Row]; - - // subtract current row from next rows to eliminate one value - for (int Row1 = Row + 1; Row1 < 3; Row1++) - { - for (int Col = 3; Col > Row; Col--) Matrix[Row1, Col] -= Matrix[Row, Col] * Matrix[Row1, Row]; - } - } - - // go up from last row and eliminate all solved values - Matrix[1, 3] -= Matrix[1, 2] * Matrix[2, 3]; - Matrix[0, 3] -= Matrix[0, 2] * Matrix[2, 3]; - Matrix[0, 3] -= Matrix[0, 1] * Matrix[1, 3]; - } - - //////////////////////////////////////////////////////////////////// - // Get image pixel color - //////////////////////////////////////////////////////////////////// - - internal bool GetModule(int Row, int Col) - { - // get module based on three finders - if (!Trans4Mode) - { - int Trans3Col = (int)Math.Round(Trans3a * Col + Trans3c * Row + Trans3e, 0, MidpointRounding.AwayFromZero); - int Trans3Row = (int)Math.Round(Trans3b * Col + Trans3d * Row + Trans3f, 0, MidpointRounding.AwayFromZero); - return BlackWhiteImage[Trans3Row, Trans3Col]; - } - - // get module based on three finders plus one alignment mark - double W = Trans4g * Col + Trans4h * Row + 1.0; - int Trans4Col = (int)Math.Round((Trans4a * Col + Trans4b * Row + Trans4c) / W, 0, MidpointRounding.AwayFromZero); - int Trans4Row = (int)Math.Round((Trans4d * Col + Trans4e * Row + Trans4f) / W, 0, MidpointRounding.AwayFromZero); - return BlackWhiteImage[Trans4Row, Trans4Col]; - } - - //////////////////////////////////////////////////////////////////// - // search row by row for finders blocks - //////////////////////////////////////////////////////////////////// - - internal bool FindAlignmentMark(Corner Corner) - { - // alignment mark estimated position - int AlignRow = QRCodeDimension - 7; - int AlignCol = QRCodeDimension - 7; - int ImageCol = (int)Math.Round(Trans3a * AlignCol + Trans3c * AlignRow + Trans3e, 0, MidpointRounding.AwayFromZero); - int ImageRow = (int)Math.Round(Trans3b * AlignCol + Trans3d * AlignRow + Trans3f, 0, MidpointRounding.AwayFromZero); - - - // search area - int Side = (int)Math.Round(ALIGNMENT_SEARCH_AREA * (Corner.TopLineLength + Corner.LeftLineLength), 0, MidpointRounding.AwayFromZero); - - int AreaLeft = ImageCol - Side / 2; - int AreaTop = ImageRow - Side / 2; - int AreaWidth = Side; - int AreaHeight = Side; - - - // horizontal search for finders - if (!HorizontalAlignmentSearch(AreaLeft, AreaTop, AreaWidth, AreaHeight)) return false; - - // vertical search for finders - VerticalAlignmentSearch(AreaLeft, AreaTop, AreaWidth, AreaHeight); - - // remove unused alignment entries - if (!RemoveUnusedAlignMarks()) return false; - - // successful exit - return true; - } - - internal void SetTransMatrix(Corner Corner, double ImageAlignRow, double ImageAlignCol) - { - // top right and bottom left QR code position - int FarFinder = QRCodeDimension - 4; - int FarAlign = QRCodeDimension - 7; - - double[,] Matrix = new double[8, 9]; - - Matrix[0, 0] = 3.0; - Matrix[0, 1] = 3.0; - Matrix[0, 2] = 1.0; - Matrix[0, 6] = -3.0 * Corner.TopLeftFinder.Col; - Matrix[0, 7] = -3.0 * Corner.TopLeftFinder.Col; - Matrix[0, 8] = Corner.TopLeftFinder.Col; - - Matrix[1, 0] = FarFinder; - Matrix[1, 1] = 3.0; - Matrix[1, 2] = 1.0; - Matrix[1, 6] = -FarFinder * Corner.TopRightFinder.Col; - Matrix[1, 7] = -3.0 * Corner.TopRightFinder.Col; - Matrix[1, 8] = Corner.TopRightFinder.Col; - - Matrix[2, 0] = 3.0; - Matrix[2, 1] = FarFinder; - Matrix[2, 2] = 1.0; - Matrix[2, 6] = -3.0 * Corner.BottomLeftFinder.Col; - Matrix[2, 7] = -FarFinder * Corner.BottomLeftFinder.Col; - Matrix[2, 8] = Corner.BottomLeftFinder.Col; - - Matrix[3, 0] = FarAlign; - Matrix[3, 1] = FarAlign; - Matrix[3, 2] = 1.0; - Matrix[3, 6] = -FarAlign * ImageAlignCol; - Matrix[3, 7] = -FarAlign * ImageAlignCol; - Matrix[3, 8] = ImageAlignCol; - - Matrix[4, 3] = 3.0; - Matrix[4, 4] = 3.0; - Matrix[4, 5] = 1.0; - Matrix[4, 6] = -3.0 * Corner.TopLeftFinder.Row; - Matrix[4, 7] = -3.0 * Corner.TopLeftFinder.Row; - Matrix[4, 8] = Corner.TopLeftFinder.Row; - - Matrix[5, 3] = FarFinder; - Matrix[5, 4] = 3.0; - Matrix[5, 5] = 1.0; - Matrix[5, 6] = -FarFinder * Corner.TopRightFinder.Row; - Matrix[5, 7] = -3.0 * Corner.TopRightFinder.Row; - Matrix[5, 8] = Corner.TopRightFinder.Row; - - Matrix[6, 3] = 3.0; - Matrix[6, 4] = FarFinder; - Matrix[6, 5] = 1.0; - Matrix[6, 6] = -3.0 * Corner.BottomLeftFinder.Row; - Matrix[6, 7] = -FarFinder * Corner.BottomLeftFinder.Row; - Matrix[6, 8] = Corner.BottomLeftFinder.Row; - - Matrix[7, 3] = FarAlign; - Matrix[7, 4] = FarAlign; - Matrix[7, 5] = 1.0; - Matrix[7, 6] = -FarAlign * ImageAlignRow; - Matrix[7, 7] = -FarAlign * ImageAlignRow; - Matrix[7, 8] = ImageAlignRow; - - for (int Row = 0; Row < 8; Row++) - { - // If the element is zero, make it non zero by adding another row - if (Matrix[Row, Row] == 0) - { - int Row1; - for (Row1 = Row + 1; Row1 < 8 && Matrix[Row1, Row] == 0; Row1++) ; - if (Row1 == 8) throw new ApplicationException("Solve linear equations failed"); - - for (int Col = Row; Col < 9; Col++) Matrix[Row, Col] += Matrix[Row1, Col]; - } - - // make the diagonal element 1.0 - for (int Col = 8; Col > Row; Col--) Matrix[Row, Col] /= Matrix[Row, Row]; - - // subtract current row from next rows to eliminate one value - for (int Row1 = Row + 1; Row1 < 8; Row1++) - { - for (int Col = 8; Col > Row; Col--) Matrix[Row1, Col] -= Matrix[Row, Col] * Matrix[Row1, Row]; - } - } - - // go up from last row and eliminate all solved values - for (int Col = 7; Col > 0; Col--) for (int Row = Col - 1; Row >= 0; Row--) - { - Matrix[Row, 8] -= Matrix[Row, Col] * Matrix[Col, 8]; - } - - Trans4a = Matrix[0, 8]; - Trans4b = Matrix[1, 8]; - Trans4c = Matrix[2, 8]; - Trans4d = Matrix[3, 8]; - Trans4e = Matrix[4, 8]; - Trans4f = Matrix[5, 8]; - Trans4g = Matrix[6, 8]; - Trans4h = Matrix[7, 8]; - - // set trans 4 mode - Trans4Mode = true; - } - - //////////////////////////////////////////////////////////////////// - // Get version code bits top right - //////////////////////////////////////////////////////////////////// - - internal int GetVersionOne() - { - int VersionCode = 0; - for (int Index = 0; Index < 18; Index++) - { - if (GetModule(Index / 3, QRCodeDimension - 11 + (Index % 3))) VersionCode |= 1 << Index; - } - return TestVersionCode(VersionCode); - } - - //////////////////////////////////////////////////////////////////// - // Get version code bits bottom left - //////////////////////////////////////////////////////////////////// - - internal int GetVersionTwo() - { - int VersionCode = 0; - for (int Index = 0; Index < 18; Index++) - { - if (GetModule(QRCodeDimension - 11 + (Index % 3), Index / 3)) VersionCode |= 1 << Index; - } - return TestVersionCode(VersionCode); - } - - //////////////////////////////////////////////////////////////////// - // Test version code bits - //////////////////////////////////////////////////////////////////// - - internal int TestVersionCode(int VersionCode) - { - // format info - int Code = VersionCode >> 12; - - // test for exact match - if (Code >= 7 && Code <= 40 && StaticTables.VersionCodeArray[Code - 7] == VersionCode) - { - return Code; - } - - // look for a match - int BestInfo = 0; - int Error = int.MaxValue; - for (int Index = 0; Index < 34; Index++) - { - // test for exact match - int ErrorBits = StaticTables.VersionCodeArray[Index] ^ VersionCode; - if (ErrorBits == 0) return VersionCode >> 12; - - // count errors - int ErrorCount = CountBits(ErrorBits); - - // save best result - if (ErrorCount < Error) - { - Error = ErrorCount; - BestInfo = Index; - } - } - - return Error <= 3 ? BestInfo + 7 : 0; - } - - //////////////////////////////////////////////////////////////////// - // Get format info around top left corner - //////////////////////////////////////////////////////////////////// - - public int GetFormatInfoOne() - { - int Info = 0; - for (int Index = 0; Index < 15; Index++) - { - if (GetModule(StaticTables.FormatInfoOne[Index, 0], StaticTables.FormatInfoOne[Index, 1])) Info |= 1 << Index; - } - return TestFormatInfo(Info); - } - - //////////////////////////////////////////////////////////////////// - // Get format info around top right and bottom left corners - //////////////////////////////////////////////////////////////////// - - internal int GetFormatInfoTwo() - { - int Info = 0; - for (int Index = 0; Index < 15; Index++) - { - int Row = StaticTables.FormatInfoTwo[Index, 0]; - if (Row < 0) Row += QRCodeDimension; - int Col = StaticTables.FormatInfoTwo[Index, 1]; - if (Col < 0) Col += QRCodeDimension; - if (GetModule(Row, Col)) Info |= 1 << Index; - } - return TestFormatInfo(Info); - } - - //////////////////////////////////////////////////////////////////// - // Test format info bits - //////////////////////////////////////////////////////////////////// - - internal int TestFormatInfo(int FormatInfo) - { - // format info - int Info = (FormatInfo ^ 0x5412) >> 10; - - // test for exact match - if (StaticTables.FormatInfoArray[Info] == FormatInfo) return Info; - - // look for a match - int BestInfo = 0; - int Error = int.MaxValue; - for (int Index = 0; Index < 32; Index++) - { - int ErrorCount = CountBits(StaticTables.FormatInfoArray[Index] ^ FormatInfo); - if (ErrorCount < Error) - { - Error = ErrorCount; - BestInfo = Index; - } - } - - return Error <= 3 ? BestInfo : -1; - } - - //////////////////////////////////////////////////////////////////// - // Count Bits - //////////////////////////////////////////////////////////////////// - - internal int CountBits(int Value) - { - int Count = 0; - for (int Mask = 0x4000; Mask != 0; Mask >>= 1) if ((Value & Mask) != 0) Count++; - return Count; - } - - //////////////////////////////////////////////////////////////////// - // Convert image to qr code matrix and test fixed modules - //////////////////////////////////////////////////////////////////// - - internal void ConvertImageToMatrix() - { - // loop for all modules - int FixedCount = 0; - int ErrorCount = 0; - for (int Row = 0; Row < QRCodeDimension; Row++) for (int Col = 0; Col < QRCodeDimension; Col++) - { - // the module (Row, Col) is not a fixed module - if ((BaseMatrix[Row, Col] & StaticTables.Fixed) == 0) - { - if (GetModule(Row, Col)) BaseMatrix[Row, Col] |= StaticTables.Black; - } - - // fixed module - else - { - // total fixed modules - FixedCount++; - - // test for error - if ((GetModule(Row, Col) ? StaticTables.Black : StaticTables.White) != (BaseMatrix[Row, Col] & 1)) ErrorCount++; - } - } - - if (ErrorCount > FixedCount * ErrCorrPercent[(int)ErrorCorrection] / 100) throw new ApplicationException("Fixed modules error"); - return; - } - - //////////////////////////////////////////////////////////////////// - // Unload matrix data from base matrix - //////////////////////////////////////////////////////////////////// - internal void UnloadDataFromMatrix() - { - // input array pointer initialization - int Ptr = 0; - int PtrEnd = 8 * MaxCodewords; - CodewordsArray = new byte[MaxCodewords]; - - // bottom right corner of output matrix - int Row = QRCodeDimension - 1; - int Col = QRCodeDimension - 1; - - // step state - int State = 0; - for (; ; ) - { - // current module is data - if ((MaskMatrix[Row, Col] & StaticTables.NonData) == 0) - { - // unload current module with - if ((MaskMatrix[Row, Col] & 1) != 0) CodewordsArray[Ptr >> 3] |= (byte)(1 << (7 - (Ptr & 7))); - if (++Ptr == PtrEnd) break; - } - - // current module is non data and vertical timing line condition is on - else if (Col == 6) Col--; - - // update matrix position to next module - switch (State) - { - // going up: step one to the left - case 0: - Col--; - State = 1; - continue; - - // going up: step one row up and one column to the right - case 1: - Col++; - Row--; - // we are not at the top, go to state 0 - if (Row >= 0) - { - State = 0; - continue; - } - // we are at the top, step two columns to the left and start going down - Col -= 2; - Row = 0; - State = 2; - continue; - - // going down: step one to the left - case 2: - Col--; - State = 3; - continue; - - // going down: step one row down and one column to the right - case 3: - Col++; - Row++; - // we are not at the bottom, go to state 2 - if (Row < QRCodeDimension) - { - State = 2; - continue; - } - // we are at the bottom, step two columns to the left and start going up - Col -= 2; - Row = QRCodeDimension - 1; - State = 0; - continue; - } - } - } - - //////////////////////////////////////////////////////////////////// - // Restore interleave data and error correction blocks - //////////////////////////////////////////////////////////////////// - - internal void RestoreBlocks() - { - // allocate temp codewords array - byte[] TempArray = new byte[MaxCodewords]; - - // total blocks - int TotalBlocks = BlocksGroup1 + BlocksGroup2; - - // create array of data blocks starting point - int[] Start = new int[TotalBlocks]; - for (int Index = 1; Index < TotalBlocks; Index++) Start[Index] = Start[Index - 1] + (Index <= BlocksGroup1 ? DataCodewordsGroup1 : DataCodewordsGroup2); - - // step one. iterleave base on group one length - int PtrEnd = DataCodewordsGroup1 * TotalBlocks; - - // restore group one and two - int Ptr; - int Block = 0; - for (Ptr = 0; Ptr < PtrEnd; Ptr++) - { - TempArray[Start[Block]] = CodewordsArray[Ptr]; - Start[Block]++; - Block++; - if (Block == TotalBlocks) Block = 0; - } - - // restore group two - if (DataCodewordsGroup2 > DataCodewordsGroup1) - { - // step one. iterleave base on group one length - PtrEnd = MaxDataCodewords; - - Block = BlocksGroup1; - for (; Ptr < PtrEnd; Ptr++) - { - TempArray[Start[Block]] = CodewordsArray[Ptr]; - Start[Block]++; - Block++; - if (Block == TotalBlocks) Block = BlocksGroup1; - } - } - - // create array of error correction blocks starting point - Start[0] = MaxDataCodewords; - for (int Index = 1; Index < TotalBlocks; Index++) Start[Index] = Start[Index - 1] + ErrCorrCodewords; - - // restore all groups - PtrEnd = MaxCodewords; - Block = 0; - for (; Ptr < PtrEnd; Ptr++) - { - TempArray[Start[Block]] = CodewordsArray[Ptr]; - Start[Block]++; - Block++; - if (Block == TotalBlocks) Block = 0; - } - - // save result - CodewordsArray = TempArray; - } - - //////////////////////////////////////////////////////////////////// - // Calculate Error Correction - //////////////////////////////////////////////////////////////////// - - protected void CalculateErrorCorrection() - { - // total error count - int TotalErrorCount = 0; - - // set generator polynomial array - byte[] Generator = StaticTables.GenArray[ErrCorrCodewords - 7]; - - // error correcion calculation buffer - int BufSize = Math.Max(DataCodewordsGroup1, DataCodewordsGroup2) + ErrCorrCodewords; - byte[] ErrCorrBuff = new byte[BufSize]; - - // initial number of data codewords - int DataCodewords = DataCodewordsGroup1; - int BuffLen = DataCodewords + ErrCorrCodewords; - - // codewords pointer - int DataCodewordsPtr = 0; - - // codewords buffer error correction pointer - int CodewordsArrayErrCorrPtr = MaxDataCodewords; - - // loop one block at a time - int TotalBlocks = BlocksGroup1 + BlocksGroup2; - for (int BlockNumber = 0; BlockNumber < TotalBlocks; BlockNumber++) - { - // switch to group2 data codewords - if (BlockNumber == BlocksGroup1) - { - DataCodewords = DataCodewordsGroup2; - BuffLen = DataCodewords + ErrCorrCodewords; - } - - // copy next block of codewords to the buffer and clear the remaining part - Array.Copy(CodewordsArray, DataCodewordsPtr, ErrCorrBuff, 0, DataCodewords); - Array.Copy(CodewordsArray, CodewordsArrayErrCorrPtr, ErrCorrBuff, DataCodewords, ErrCorrCodewords); - - // make a duplicate - byte[] CorrectionBuffer = (byte[])ErrCorrBuff.Clone(); - - // error correction polynomial division - ReedSolomon.PolynominalDivision(ErrCorrBuff, BuffLen, Generator, ErrCorrCodewords); - - // test for error - int Index; - for (Index = 0; Index < ErrCorrCodewords && ErrCorrBuff[DataCodewords + Index] == 0; Index++) ; - if (Index < ErrCorrCodewords) - { - // correct the error - int ErrorCount = ReedSolomon.CorrectData(CorrectionBuffer, BuffLen, ErrCorrCodewords); - if (ErrorCount <= 0) - { - throw new ApplicationException("Data is damaged. Error correction failed"); - } - - TotalErrorCount += ErrorCount; - - // fix the data - Array.Copy(CorrectionBuffer, 0, CodewordsArray, DataCodewordsPtr, DataCodewords); - } - - // update codewords array to next buffer - DataCodewordsPtr += DataCodewords; - - // update pointer - CodewordsArrayErrCorrPtr += ErrCorrCodewords; - } - } - - //////////////////////////////////////////////////////////////////// - // Convert bit array to byte array - //////////////////////////////////////////////////////////////////// - - internal byte[] DecodeData() - { - // bit buffer initial condition - BitBuffer = (UInt32)((CodewordsArray[0] << 24) | (CodewordsArray[1] << 16) | (CodewordsArray[2] << 8) | CodewordsArray[3]); - BitBufferLen = 32; - CodewordsPtr = 4; - - // allocate data byte list - List DataSeg = new List(); - - // data might be made of blocks - for (; ; ) - { - // first 4 bits is mode indicator - EncodingMode EncodingMode = (EncodingMode)ReadBitsFromCodewordsArray(4); - - // end of data - if (EncodingMode <= 0) break; - - // read data length - int DataLength = ReadBitsFromCodewordsArray(DataLengthBits(EncodingMode)); - if (DataLength < 0) - { - throw new ApplicationException("Premature end of data (DataLengh)"); - } - - // save start of segment - int SegStart = DataSeg.Count; - - // switch based on encode mode - // numeric code indicator is 0001, alpha numeric 0010, byte 0100 - switch (EncodingMode) - { - // numeric mode - case EncodingMode.Numeric: - // encode digits in groups of 2 - int NumericEnd = (DataLength / 3) * 3; - for (int Index = 0; Index < NumericEnd; Index += 3) - { - int Temp = ReadBitsFromCodewordsArray(10); - if (Temp < 0) - { - throw new ApplicationException("Premature end of data (Numeric 1)"); - } - DataSeg.Add(StaticTables.DecodingTable[Temp / 100]); - DataSeg.Add(StaticTables.DecodingTable[(Temp % 100) / 10]); - DataSeg.Add(StaticTables.DecodingTable[Temp % 10]); - } - - // we have one character remaining - if (DataLength - NumericEnd == 1) - { - int Temp = ReadBitsFromCodewordsArray(4); - if (Temp < 0) - { - throw new ApplicationException("Premature end of data (Numeric 2)"); - } - DataSeg.Add(StaticTables.DecodingTable[Temp]); - } - - // we have two character remaining - else if (DataLength - NumericEnd == 2) - { - int Temp = ReadBitsFromCodewordsArray(7); - if (Temp < 0) - { - throw new ApplicationException("Premature end of data (Numeric 3)"); - } - DataSeg.Add(StaticTables.DecodingTable[Temp / 10]); - DataSeg.Add(StaticTables.DecodingTable[Temp % 10]); - } - break; - - // alphanumeric mode - case EncodingMode.AlphaNumeric: - // encode digits in groups of 2 - int AlphaNumEnd = (DataLength / 2) * 2; - for (int Index = 0; Index < AlphaNumEnd; Index += 2) - { - int Temp = ReadBitsFromCodewordsArray(11); - if (Temp < 0) - { - throw new ApplicationException("Premature end of data (Alpha Numeric 1)"); - } - DataSeg.Add(StaticTables.DecodingTable[Temp / 45]); - DataSeg.Add(StaticTables.DecodingTable[Temp % 45]); - } - - // we have one character remaining - if (DataLength - AlphaNumEnd == 1) - { - int Temp = ReadBitsFromCodewordsArray(6); - if (Temp < 0) - { - throw new ApplicationException("Premature end of data (Alpha Numeric 2)"); - } - DataSeg.Add(StaticTables.DecodingTable[Temp]); - } - break; - - // byte mode - case EncodingMode.Byte: - // append the data after mode and character count - for (int Index = 0; Index < DataLength; Index++) - { - int Temp = ReadBitsFromCodewordsArray(8); - if (Temp < 0) - { - throw new ApplicationException("Premature end of data (byte mode)"); - } - DataSeg.Add((byte)Temp); - } - break; - - default: - throw new ApplicationException(string.Format("Encoding mode not supported {0}", EncodingMode.ToString())); - } - - if (DataLength != DataSeg.Count - SegStart) throw new ApplicationException("Data encoding length in error"); - } - - // save data - return DataSeg.ToArray(); - } - - //////////////////////////////////////////////////////////////////// - // Read data from codeword array - //////////////////////////////////////////////////////////////////// - - internal int ReadBitsFromCodewordsArray(int Bits) - { - if (Bits > BitBufferLen) return -1; - int Data = (int)(BitBuffer >> (32 - Bits)); - BitBuffer <<= Bits; - BitBufferLen -= Bits; - while (BitBufferLen <= 24 && CodewordsPtr < MaxDataCodewords) - { - BitBuffer |= (UInt32)(CodewordsArray[CodewordsPtr++] << (24 - BitBufferLen)); - BitBufferLen += 8; - } - return Data; - } - //////////////////////////////////////////////////////////////////// - // Set encoded data bits length - //////////////////////////////////////////////////////////////////// - - internal int DataLengthBits(EncodingMode EncodingMode) - { - // Data length bits - switch (EncodingMode) - { - // numeric mode - case EncodingMode.Numeric: - return QRCodeVersion < 10 ? 10 : (QRCodeVersion < 27 ? 12 : 14); - - // alpha numeric mode - case EncodingMode.AlphaNumeric: - return QRCodeVersion < 10 ? 9 : (QRCodeVersion < 27 ? 11 : 13); - - // byte mode - case EncodingMode.Byte: - return QRCodeVersion < 10 ? 8 : 16; - } - throw new ApplicationException("Encoding mode error"); - } - - //////////////////////////////////////////////////////////////////// - // Set data and error correction codewords length - //////////////////////////////////////////////////////////////////// - - internal void SetDataCodewordsLength() - { - // index shortcut - int BlockInfoIndex = (QRCodeVersion - 1) * 4 + (int)ErrorCorrection; - - // Number of blocks in group 1 - BlocksGroup1 = StaticTables.ECBlockInfo[BlockInfoIndex, StaticTables.BLOCKS_GROUP1]; - - // Number of data codewords in blocks of group 1 - DataCodewordsGroup1 = StaticTables.ECBlockInfo[BlockInfoIndex, StaticTables.DATA_CODEWORDS_GROUP1]; - - // Number of blocks in group 2 - BlocksGroup2 = StaticTables.ECBlockInfo[BlockInfoIndex, StaticTables.BLOCKS_GROUP2]; - - // Number of data codewords in blocks of group 2 - DataCodewordsGroup2 = StaticTables.ECBlockInfo[BlockInfoIndex, StaticTables.DATA_CODEWORDS_GROUP2]; - - // Total number of data codewords for this version and EC level - MaxDataCodewords = BlocksGroup1 * DataCodewordsGroup1 + BlocksGroup2 * DataCodewordsGroup2; - MaxDataBits = 8 * MaxDataCodewords; - - // total data plus error correction bits - MaxCodewords = StaticTables.MaxCodewordsArray[QRCodeVersion]; - - // Error correction codewords per block - ErrCorrCodewords = (MaxCodewords - MaxDataCodewords) / (BlocksGroup1 + BlocksGroup2); - - // exit - return; - } - - //////////////////////////////////////////////////////////////////// - // Format info to error correction code - //////////////////////////////////////////////////////////////////// - - internal ErrorCorrection FormatInfoToErrCode(int Info) - { - return (ErrorCorrection)(Info ^ 1); - } - - //////////////////////////////////////////////////////////////////// - // Build Base Matrix - //////////////////////////////////////////////////////////////////// - - internal void BuildBaseMatrix() - { - // allocate base matrix - BaseMatrix = new byte[QRCodeDimension + 5, QRCodeDimension + 5]; - - // top left finder patterns - for (int Row = 0; Row < 9; Row++) for (int Col = 0; Col < 9; Col++) BaseMatrix[Row, Col] = StaticTables.FinderPatternTopLeft[Row, Col]; - - // top right finder patterns - int Pos = QRCodeDimension - 8; - for (int Row = 0; Row < 9; Row++) for (int Col = 0; Col < 8; Col++) BaseMatrix[Row, Pos + Col] = StaticTables.FinderPatternTopRight[Row, Col]; - - // bottom left finder patterns - for (int Row = 0; Row < 8; Row++) for (int Col = 0; Col < 9; Col++) BaseMatrix[Pos + Row, Col] = StaticTables.FinderPatternBottomLeft[Row, Col]; - - // Timing pattern - for (int Z = 8; Z < QRCodeDimension - 8; Z++) BaseMatrix[Z, 6] = BaseMatrix[6, Z] = (Z & 1) == 0 ? StaticTables.FixedBlack : StaticTables.FixedWhite; - - // alignment pattern - if (QRCodeVersion > 1) - { - byte[] AlignPos = StaticTables.AlignmentPositionArray[QRCodeVersion]; - int AlignmentDimension = AlignPos.Length; - for (int Row = 0; Row < AlignmentDimension; Row++) for (int Col = 0; Col < AlignmentDimension; Col++) - { - if (Col == 0 && Row == 0 || Col == AlignmentDimension - 1 && Row == 0 || Col == 0 && Row == AlignmentDimension - 1) continue; - - int PosRow = AlignPos[Row]; - int PosCol = AlignPos[Col]; - for (int ARow = -2; ARow < 3; ARow++) for (int ACol = -2; ACol < 3; ACol++) - { - BaseMatrix[PosRow + ARow, PosCol + ACol] = StaticTables.AlignmentPattern[ARow + 2, ACol + 2]; - } - } - } - - // reserve version information - if (QRCodeVersion >= 7) - { - // position of 3 by 6 rectangles - Pos = QRCodeDimension - 11; - - // top right - for (int Row = 0; Row < 6; Row++) for (int Col = 0; Col < 3; Col++) BaseMatrix[Row, Pos + Col] = StaticTables.FormatWhite; - - // bottom right - for (int Col = 0; Col < 6; Col++) for (int Row = 0; Row < 3; Row++) BaseMatrix[Pos + Row, Col] = StaticTables.FormatWhite; - } - } - - //////////////////////////////////////////////////////////////////// - // Apply Mask - //////////////////////////////////////////////////////////////////// - - internal void ApplyMask(int Mask) - { - MaskMatrix = (byte[,])BaseMatrix.Clone(); - switch (Mask) - { - case 0: - ApplyMask0(); - break; - - case 1: - ApplyMask1(); - break; - - case 2: - ApplyMask2(); - break; - - case 3: - ApplyMask3(); - break; - - case 4: - ApplyMask4(); - break; - - case 5: - ApplyMask5(); - break; - - case 6: - ApplyMask6(); - break; - - case 7: - ApplyMask7(); - break; - } - return; - } - - //////////////////////////////////////////////////////////////////// - // Apply Mask 0 - // (row + column) % 2 == 0 - //////////////////////////////////////////////////////////////////// - - internal void ApplyMask0() - { - for (int Row = 0; Row < QRCodeDimension; Row += 2) for (int Col = 0; Col < QRCodeDimension; Col += 2) - { - if ((MaskMatrix[Row, Col] & StaticTables.NonData) == 0) MaskMatrix[Row, Col] ^= 1; - if ((MaskMatrix[Row + 1, Col + 1] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col + 1] ^= 1; - } - return; - } - - //////////////////////////////////////////////////////////////////// - // Apply Mask 1 - // row % 2 == 0 - //////////////////////////////////////////////////////////////////// - - internal void ApplyMask1() - { - for (int Row = 0; Row < QRCodeDimension; Row += 2) for (int Col = 0; Col < QRCodeDimension; Col++) - if ((MaskMatrix[Row, Col] & StaticTables.NonData) == 0) MaskMatrix[Row, Col] ^= 1; - return; - } - - //////////////////////////////////////////////////////////////////// - // Apply Mask 2 - // column % 3 == 0 - //////////////////////////////////////////////////////////////////// - - internal void ApplyMask2() - { - for (int Row = 0; Row < QRCodeDimension; Row++) for (int Col = 0; Col < QRCodeDimension; Col += 3) - if ((MaskMatrix[Row, Col] & StaticTables.NonData) == 0) MaskMatrix[Row, Col] ^= 1; - return; - } - - //////////////////////////////////////////////////////////////////// - // Apply Mask 3 - // (row + column) % 3 == 0 - //////////////////////////////////////////////////////////////////// - - internal void ApplyMask3() - { - for (int Row = 0; Row < QRCodeDimension; Row += 3) for (int Col = 0; Col < QRCodeDimension; Col += 3) - { - if ((MaskMatrix[Row, Col] & StaticTables.NonData) == 0) MaskMatrix[Row, Col] ^= 1; - if ((MaskMatrix[Row + 1, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col + 2] ^= 1; - if ((MaskMatrix[Row + 2, Col + 1] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 1] ^= 1; - } - } - - //////////////////////////////////////////////////////////////////// - // Apply Mask 4 - // ((row / 2) + (column / 3)) % 2 == 0 - //////////////////////////////////////////////////////////////////// - - internal void ApplyMask4() - { - for (int Row = 0; Row < QRCodeDimension; Row += 4) for (int Col = 0; Col < QRCodeDimension; Col += 6) - { - if ((MaskMatrix[Row, Col] & StaticTables.NonData) == 0) MaskMatrix[Row, Col] ^= 1; - if ((MaskMatrix[Row, Col + 1] & StaticTables.NonData) == 0) MaskMatrix[Row, Col + 1] ^= 1; - if ((MaskMatrix[Row, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row, Col + 2] ^= 1; - - if ((MaskMatrix[Row + 1, Col] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col] ^= 1; - if ((MaskMatrix[Row + 1, Col + 1] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col + 1] ^= 1; - if ((MaskMatrix[Row + 1, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col + 2] ^= 1; - - if ((MaskMatrix[Row + 2, Col + 3] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 3] ^= 1; - if ((MaskMatrix[Row + 2, Col + 4] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 4] ^= 1; - if ((MaskMatrix[Row + 2, Col + 5] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 5] ^= 1; - - if ((MaskMatrix[Row + 3, Col + 3] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 3] ^= 1; - if ((MaskMatrix[Row + 3, Col + 4] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 4] ^= 1; - if ((MaskMatrix[Row + 3, Col + 5] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 5] ^= 1; - } - return; - } - - //////////////////////////////////////////////////////////////////// - // Apply Mask 5 - // ((row * column) % 2) + ((row * column) % 3) == 0 - //////////////////////////////////////////////////////////////////// - - internal void ApplyMask5() - { - for (int Row = 0; Row < QRCodeDimension; Row += 6) for (int Col = 0; Col < QRCodeDimension; Col += 6) - { - for (int Delta = 0; Delta < 6; Delta++) if ((MaskMatrix[Row, Col + Delta] & StaticTables.NonData) == 0) MaskMatrix[Row, Col + Delta] ^= 1; - for (int Delta = 1; Delta < 6; Delta++) if ((MaskMatrix[Row + Delta, Col] & StaticTables.NonData) == 0) MaskMatrix[Row + Delta, Col] ^= 1; - if ((MaskMatrix[Row + 2, Col + 3] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 3] ^= 1; - if ((MaskMatrix[Row + 3, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 2] ^= 1; - if ((MaskMatrix[Row + 3, Col + 4] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 4] ^= 1; - if ((MaskMatrix[Row + 4, Col + 3] & StaticTables.NonData) == 0) MaskMatrix[Row + 4, Col + 3] ^= 1; - } - return; - } - - //////////////////////////////////////////////////////////////////// - // Apply Mask 6 - // (((row * column) % 2) + ((row * column) mod 3)) mod 2 == 0 - //////////////////////////////////////////////////////////////////// - - internal void ApplyMask6() - { - for (int Row = 0; Row < QRCodeDimension; Row += 6) for (int Col = 0; Col < QRCodeDimension; Col += 6) - { - for (int Delta = 0; Delta < 6; Delta++) if ((MaskMatrix[Row, Col + Delta] & StaticTables.NonData) == 0) MaskMatrix[Row, Col + Delta] ^= 1; - for (int Delta = 1; Delta < 6; Delta++) if ((MaskMatrix[Row + Delta, Col] & StaticTables.NonData) == 0) MaskMatrix[Row + Delta, Col] ^= 1; - if ((MaskMatrix[Row + 1, Col + 1] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col + 1] ^= 1; - if ((MaskMatrix[Row + 1, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col + 2] ^= 1; - if ((MaskMatrix[Row + 2, Col + 1] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 1] ^= 1; - if ((MaskMatrix[Row + 2, Col + 3] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 3] ^= 1; - if ((MaskMatrix[Row + 2, Col + 4] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 4] ^= 1; - if ((MaskMatrix[Row + 3, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 2] ^= 1; - if ((MaskMatrix[Row + 3, Col + 4] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 4] ^= 1; - if ((MaskMatrix[Row + 4, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row + 4, Col + 2] ^= 1; - if ((MaskMatrix[Row + 4, Col + 3] & StaticTables.NonData) == 0) MaskMatrix[Row + 4, Col + 3] ^= 1; - if ((MaskMatrix[Row + 4, Col + 5] & StaticTables.NonData) == 0) MaskMatrix[Row + 4, Col + 5] ^= 1; - if ((MaskMatrix[Row + 5, Col + 4] & StaticTables.NonData) == 0) MaskMatrix[Row + 5, Col + 4] ^= 1; - if ((MaskMatrix[Row + 5, Col + 5] & StaticTables.NonData) == 0) MaskMatrix[Row + 5, Col + 5] ^= 1; - } - return; - } - - //////////////////////////////////////////////////////////////////// - // Apply Mask 7 - // (((row + column) % 2) + ((row * column) mod 3)) mod 2 == 0 - //////////////////////////////////////////////////////////////////// - - internal void ApplyMask7() - { - for (int Row = 0; Row < QRCodeDimension; Row += 6) for (int Col = 0; Col < QRCodeDimension; Col += 6) - { - if ((MaskMatrix[Row, Col] & StaticTables.NonData) == 0) MaskMatrix[Row, Col] ^= 1; - if ((MaskMatrix[Row, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row, Col + 2] ^= 1; - if ((MaskMatrix[Row, Col + 4] & StaticTables.NonData) == 0) MaskMatrix[Row, Col + 4] ^= 1; - - if ((MaskMatrix[Row + 1, Col + 3] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col + 3] ^= 1; - if ((MaskMatrix[Row + 1, Col + 4] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col + 4] ^= 1; - if ((MaskMatrix[Row + 1, Col + 5] & StaticTables.NonData) == 0) MaskMatrix[Row + 1, Col + 5] ^= 1; - - if ((MaskMatrix[Row + 2, Col] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col] ^= 1; - if ((MaskMatrix[Row + 2, Col + 4] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 4] ^= 1; - if ((MaskMatrix[Row + 2, Col + 5] & StaticTables.NonData) == 0) MaskMatrix[Row + 2, Col + 5] ^= 1; - - if ((MaskMatrix[Row + 3, Col + 1] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 1] ^= 1; - if ((MaskMatrix[Row + 3, Col + 3] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 3] ^= 1; - if ((MaskMatrix[Row + 3, Col + 5] & StaticTables.NonData) == 0) MaskMatrix[Row + 3, Col + 5] ^= 1; - - if ((MaskMatrix[Row + 4, Col] & StaticTables.NonData) == 0) MaskMatrix[Row + 4, Col] ^= 1; - if ((MaskMatrix[Row + 4, Col + 1] & StaticTables.NonData) == 0) MaskMatrix[Row + 4, Col + 1] ^= 1; - if ((MaskMatrix[Row + 4, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row + 4, Col + 2] ^= 1; - - if ((MaskMatrix[Row + 5, Col + 1] & StaticTables.NonData) == 0) MaskMatrix[Row + 5, Col + 1] ^= 1; - if ((MaskMatrix[Row + 5, Col + 2] & StaticTables.NonData) == 0) MaskMatrix[Row + 5, Col + 2] ^= 1; - if ((MaskMatrix[Row + 5, Col + 3] & StaticTables.NonData) == 0) MaskMatrix[Row + 5, Col + 3] ^= 1; - } - return; - } - } - - /// - /// QR Code error correction code enumeration - /// - public enum ErrorCorrection - { - /// - /// Low (01) - /// - L, - - /// - /// Medium (00) - /// - M, - - /// - /// Medium-high (11) - /// - Q, - - /// - /// High (10) - /// - H, - } - - /// - /// QR Code encoding modes - /// - public enum EncodingMode - { - /// - /// Terminator - /// - Terminator, - - /// - /// Numeric - /// - Numeric, - - /// - /// Alpha numeric - /// - AlphaNumeric, - - /// - /// Append - /// - Append, - - /// - /// byte encoding - /// - Byte, - - /// - /// FNC1 first - /// - FNC1First, - - /// - /// Unknown encoding constant - /// - Unknown6, - - /// - /// Unknown encoding constant - /// - Unknown7, - - /// - /// Kanji encoding (not implemented by this software) - /// - Kanji, - - /// - /// FNC1 second - /// - FNC1Second, - - /// - /// Unknown encoding constant - /// - Unknown10, - - /// - /// Unknown encoding constant - /// - Unknown11, - - /// - /// Unknown encoding constant - /// - Unknown12, - - /// - /// Unknown encoding constant - /// - Unknown13, - - /// - /// Unknown encoding constant - /// - Unknown14, - - /// - /// Unknown encoding constant - /// - Unknown15, - } - - public enum ImageFormatIndex - { - Png, - Jpeg, - Bmp, - Gif, - } - - internal class Finder - { - // horizontal scan - internal int Row; - internal int Col1; - internal int Col2; - internal double HModule; - - // vertical scan - internal int Col; - internal int Row1; - internal int Row2; - internal double VModule; - - internal double Distance; - internal double ModuleSize; - - /// - /// Constructor during horizontal scan - /// - internal Finder(int Row, int Col1, int Col2, double HModule) - { - this.Row = Row; - this.Col1 = Col1; - this.Col2 = Col2; - this.HModule = HModule; - Distance = double.MaxValue; - return; - } - - /// - /// Match during vertical scan - /// - internal void Match(int Col, int Row1, int Row2, double VModule) - { - // test if horizontal and vertical are not related - if (Col < Col1 || Col >= Col2 || Row < Row1 || Row >= Row2) return; - - // Module sizes must be about the same - if (Math.Min(HModule, VModule) < Math.Max(HModule, VModule) * QRDecoder.MODULE_SIZE_DEVIATION) return; - - // calculate distance - double DeltaX = Col - 0.5 * (Col1 + Col2); - double DeltaY = Row - 0.5 * (Row1 + Row2); - double Delta = Math.Sqrt(DeltaX * DeltaX + DeltaY * DeltaY); - - // distance between two points must be less than 2 pixels - if (Delta > QRDecoder.HOR_VERT_SCAN_MAX_DISTANCE) return; - - // new result is better than last result - if (Delta < Distance) - { - this.Col = Col; - this.Row1 = Row1; - this.Row2 = Row2; - this.VModule = VModule; - ModuleSize = 0.5 * (HModule + VModule); - Distance = Delta; - } - return; - } - - /// - /// Horizontal and vertical scans overlap - /// - internal bool Overlap - ( - Finder Other - ) - { - return Other.Col1 < Col2 && Other.Col2 >= Col1 && Other.Row1 < Row2 && Other.Row2 >= Row1; - } - - /// - /// Finder to string - /// - public override string ToString() - { - if (Distance == double.MaxValue) - { - return string.Format("Finder: Row: {0}, Col1: {1}, Col2: {2}, HModule: {3:0.00}", Row, Col1, Col2, HModule); - } - - return string.Format("Finder: Row: {0}, Col: {1}, Module: {2:0.00}, Distance: {3:0.00}", Row, Col, ModuleSize, Distance); - } - } - - internal class Corner - { - internal Finder TopLeftFinder; - internal Finder TopRightFinder; - internal Finder BottomLeftFinder; - - internal double TopLineDeltaX; - internal double TopLineDeltaY; - internal double TopLineLength; - internal double LeftLineDeltaX; - internal double LeftLineDeltaY; - internal double LeftLineLength; - - ///////////////////////////////////////////////////////////////////// - // QR corner constructor - ///////////////////////////////////////////////////////////////////// - - private Corner - ( - Finder TopLeftFinder, - Finder TopRightFinder, - Finder BottomLeftFinder - ) - { - // save three finders - this.TopLeftFinder = TopLeftFinder; - this.TopRightFinder = TopRightFinder; - this.BottomLeftFinder = BottomLeftFinder; - - // top line slope - TopLineDeltaX = TopRightFinder.Col - TopLeftFinder.Col; - TopLineDeltaY = TopRightFinder.Row - TopLeftFinder.Row; - - // top line length - TopLineLength = Math.Sqrt(TopLineDeltaX * TopLineDeltaX + TopLineDeltaY * TopLineDeltaY); - - // left line slope - LeftLineDeltaX = BottomLeftFinder.Col - TopLeftFinder.Col; - LeftLineDeltaY = BottomLeftFinder.Row - TopLeftFinder.Row; - - // left line length - LeftLineLength = Math.Sqrt(LeftLineDeltaX * LeftLineDeltaX + LeftLineDeltaY * LeftLineDeltaY); - return; - } - - ///////////////////////////////////////////////////////////////////// - // Test QR corner for validity - ///////////////////////////////////////////////////////////////////// - - internal static Corner CreateCorner - ( - Finder TopLeftFinder, - Finder TopRightFinder, - Finder BottomLeftFinder - ) - { - // try all three possible permutation of three finders - for (int Index = 0; Index < 3; Index++) - { - // TestCorner runs three times to test all posibilities - // rotate top left, top right and bottom left - if (Index != 0) - { - Finder Temp = TopLeftFinder; - TopLeftFinder = TopRightFinder; - TopRightFinder = BottomLeftFinder; - BottomLeftFinder = Temp; - } - - // top line slope - double TopLineDeltaX = TopRightFinder.Col - TopLeftFinder.Col; - double TopLineDeltaY = TopRightFinder.Row - TopLeftFinder.Row; - - // left line slope - double LeftLineDeltaX = BottomLeftFinder.Col - TopLeftFinder.Col; - double LeftLineDeltaY = BottomLeftFinder.Row - TopLeftFinder.Row; - - // top line length - double TopLineLength = Math.Sqrt(TopLineDeltaX * TopLineDeltaX + TopLineDeltaY * TopLineDeltaY); - - // left line length - double LeftLineLength = Math.Sqrt(LeftLineDeltaX * LeftLineDeltaX + LeftLineDeltaY * LeftLineDeltaY); - - // the short side must be at least 80% of the long side - if (Math.Min(TopLineLength, LeftLineLength) < QRDecoder.CORNER_SIDE_LENGTH_DEV * Math.Max(TopLineLength, LeftLineLength)) continue; - - // top line vector - double TopLineSin = TopLineDeltaY / TopLineLength; - double TopLineCos = TopLineDeltaX / TopLineLength; - - // rotate lines such that top line is parallel to x axis - // left line after rotation - double NewLeftX = TopLineCos * LeftLineDeltaX + TopLineSin * LeftLineDeltaY; - double NewLeftY = -TopLineSin * LeftLineDeltaX + TopLineCos * LeftLineDeltaY; - - // new left line X should be zero (or between +/- 4 deg) - if (Math.Abs(NewLeftX / LeftLineLength) > QRDecoder.CORNER_RIGHT_ANGLE_DEV) continue; - - // swap top line with left line - if (NewLeftY < 0) - { - // swap top left with bottom right - Finder TempFinder = TopRightFinder; - TopRightFinder = BottomLeftFinder; - BottomLeftFinder = TempFinder; - } - - return new Corner(TopLeftFinder, TopRightFinder, BottomLeftFinder); - } - return null; - } - - ///////////////////////////////////////////////////////////////////// - // Test QR corner for validity - ///////////////////////////////////////////////////////////////////// - - internal int InitialVersionNumber() - { - // version number based on top line - double TopModules = 7; - - // top line is mostly horizontal - if (Math.Abs(TopLineDeltaX) >= Math.Abs(TopLineDeltaY)) - { - TopModules += TopLineLength * TopLineLength / - (Math.Abs(TopLineDeltaX) * 0.5 * (TopLeftFinder.HModule + TopRightFinder.HModule)); - } - - // top line is mostly vertical - else - { - TopModules += TopLineLength * TopLineLength / - (Math.Abs(TopLineDeltaY) * 0.5 * (TopLeftFinder.VModule + TopRightFinder.VModule)); - } - - // version number based on left line - double LeftModules = 7; - - // Left line is mostly vertical - if (Math.Abs(LeftLineDeltaY) >= Math.Abs(LeftLineDeltaX)) - { - LeftModules += LeftLineLength * LeftLineLength / - (Math.Abs(LeftLineDeltaY) * 0.5 * (TopLeftFinder.VModule + BottomLeftFinder.VModule)); - } - - // left line is mostly horizontal - else - { - LeftModules += LeftLineLength * LeftLineLength / - (Math.Abs(LeftLineDeltaX) * 0.5 * (TopLeftFinder.HModule + BottomLeftFinder.HModule)); - } - - // version (there is rounding in the calculation) - int Version = ((int)Math.Round(0.5 * (TopModules + LeftModules)) - 15) / 4; - - // not a valid corner - if (Version < 1 || Version > 40) throw new ApplicationException("Corner is not valid (version number must be 1 to 40)"); - - // exit with version number - return Version; - } - } - - internal class ReedSolomon - { - private static int INCORRECTABLE_ERROR = -1; - - internal static int CorrectData(byte[] ReceivedData, int DataLength, int ErrCorrCodewords) - { - // calculate syndrome vector - int[] Syndrome = CalculateSyndrome(ReceivedData, DataLength, ErrCorrCodewords); - - // received data has no error - // note: this should not happen because we call this method only if error was detected - if (Syndrome == null) return 0; - - // Modified Berlekamp-Massey - // calculate sigma and omega - int[] Sigma = new int[ErrCorrCodewords / 2 + 2]; - int[] Omega = new int[ErrCorrCodewords / 2 + 1]; - int ErrorCount = CalculateSigmaMBM(Sigma, Omega, Syndrome, ErrCorrCodewords); - - // data cannot be corrected - if (ErrorCount <= 0) return INCORRECTABLE_ERROR; - - // look for error position using Chien search - int[] ErrorPosition = new int[ErrorCount]; - if (!ChienSearch(ErrorPosition, DataLength, ErrorCount, Sigma)) return INCORRECTABLE_ERROR; - - // correct data array based on position array - ApplyCorrection(ReceivedData, DataLength, ErrorCount, ErrorPosition, Sigma, Omega); - - // return error count before it was corrected - return ErrorCount; - } - - // Syndrome vector calculation - // S0 = R0 + R1 + R2 + .... + Rn - // S1 = R0 + R1 * A**1 + R2 * A**2 + .... + Rn * A**n - // S2 = R0 + R1 * A**2 + R2 * A**4 + .... + Rn * A**2n - // .... - // Sm = R0 + R1 * A**m + R2 * A**2m + .... + Rn * A**mn - private static int[] CalculateSyndrome(byte[] ReceivedData, int DataLength, int ErrCorrCodewords) - { - // allocate syndrome vector - int[] Syndrome = new int[ErrCorrCodewords]; - - // reset error indicator - bool Error = false; - - // syndrome[zero] special case - // Total = Data[0] + Data[1] + ... Data[n] - int Total = ReceivedData[0]; - for (int SumIndex = 1; SumIndex < DataLength; SumIndex++) Total = ReceivedData[SumIndex] ^ Total; - Syndrome[0] = Total; - if (Total != 0) Error = true; - - // all other synsromes - for (int Index = 1; Index < ErrCorrCodewords; Index++) - { - // Total = Data[0] + Data[1] * Alpha + Data[2] * Alpha ** 2 + ... Data[n] * Alpha ** n - Total = ReceivedData[0]; - for (int IndexT = 1; IndexT < DataLength; IndexT++) Total = ReceivedData[IndexT] ^ MultiplyIntByExp(Total, Index); - Syndrome[Index] = Total; - if (Total != 0) Error = true; - } - - // if there is an error return syndrome vector otherwise return null - return Error ? Syndrome : null; - } - - // Modified Berlekamp-Massey - private static int CalculateSigmaMBM(int[] Sigma, int[] Omega, int[] Syndrome, int ErrCorrCodewords) - { - int[] PolyC = new int[ErrCorrCodewords]; - int[] PolyB = new int[ErrCorrCodewords]; - PolyC[1] = 1; - PolyB[0] = 1; - int ErrorControl = 1; - int ErrorCount = 0; // L - int m = -1; - - for (int ErrCorrIndex = 0; ErrCorrIndex < ErrCorrCodewords; ErrCorrIndex++) - { - // Calculate the discrepancy - int Dis = Syndrome[ErrCorrIndex]; - for (int i = 1; i <= ErrorCount; i++) Dis ^= Multiply(PolyB[i], Syndrome[ErrCorrIndex - i]); - - if (Dis != 0) - { - int DisExp = StaticTables.IntToExp[Dis]; - int[] WorkPolyB = new int[ErrCorrCodewords]; - for (int Index = 0; Index <= ErrCorrIndex; Index++) WorkPolyB[Index] = PolyB[Index] ^ MultiplyIntByExp(PolyC[Index], DisExp); - int js = ErrCorrIndex - m; - if (js > ErrorCount) - { - m = ErrCorrIndex - ErrorCount; - ErrorCount = js; - if (ErrorCount > ErrCorrCodewords / 2) return INCORRECTABLE_ERROR; - for (int Index = 0; Index <= ErrorControl; Index++) PolyC[Index] = DivideIntByExp(PolyB[Index], DisExp); - ErrorControl = ErrorCount; - } - PolyB = WorkPolyB; - } - - // shift polynomial right one - Array.Copy(PolyC, 0, PolyC, 1, Math.Min(PolyC.Length - 1, ErrorControl)); - PolyC[0] = 0; - ErrorControl++; - } - - PolynomialMultiply(Omega, PolyB, Syndrome); - Array.Copy(PolyB, 0, Sigma, 0, Math.Min(PolyB.Length, Sigma.Length)); - return ErrorCount; - } - - // Chien search is a fast algorithm for determining roots of polynomials defined over a finite field. - // The most typical use of the Chien search is in finding the roots of error-locator polynomials - // encountered in decoding Reed-Solomon codes and BCH codes. - private static bool ChienSearch(int[] ErrorPosition, int DataLength, int ErrorCount, int[] Sigma) - { - // last error - int LastPosition = Sigma[1]; - - // one error - if (ErrorCount == 1) - { - // position is out of range - if (StaticTables.IntToExp[LastPosition] >= DataLength) return false; - - // save the only error position in position array - ErrorPosition[0] = LastPosition; - return true; - } - - // we start at last error position - int PosIndex = ErrorCount - 1; - for (int DataIndex = 0; DataIndex < DataLength; DataIndex++) - { - int DataIndexInverse = 255 - DataIndex; - int Total = 1; - for (int Index = 1; Index <= ErrorCount; Index++) Total ^= MultiplyIntByExp(Sigma[Index], (DataIndexInverse * Index) % 255); - if (Total != 0) continue; - - int Position = StaticTables.ExpToInt[DataIndex]; - LastPosition ^= Position; - ErrorPosition[PosIndex--] = Position; - if (PosIndex == 0) - { - // position is out of range - if (StaticTables.IntToExp[LastPosition] >= DataLength) return false; - ErrorPosition[0] = LastPosition; - return true; - } - } - - // search failed - return false; - } - - private static void ApplyCorrection(byte[] ReceivedData, int DataLength, int ErrorCount, int[] ErrorPosition, int[] Sigma, int[] Omega) - { - for (int ErrIndex = 0; ErrIndex < ErrorCount; ErrIndex++) - { - int ps = ErrorPosition[ErrIndex]; - int zlog = 255 - StaticTables.IntToExp[ps]; - int OmegaTotal = Omega[0]; - for (int Index = 1; Index < ErrorCount; Index++) OmegaTotal ^= MultiplyIntByExp(Omega[Index], (zlog * Index) % 255); - int SigmaTotal = Sigma[1]; - for (int j = 2; j < ErrorCount; j += 2) SigmaTotal ^= MultiplyIntByExp(Sigma[j + 1], (zlog * j) % 255); - ReceivedData[DataLength - 1 - StaticTables.IntToExp[ps]] ^= (byte)MultiplyDivide(ps, OmegaTotal, SigmaTotal); - } - } - - internal static void PolynominalDivision(byte[] Polynomial, int PolyLength, byte[] Generator, int ErrCorrCodewords) - { - int DataCodewords = PolyLength - ErrCorrCodewords; - - // error correction polynomial division - for (int Index = 0; Index < DataCodewords; Index++) - { - // current first codeword is zero - if (Polynomial[Index] == 0) continue; - - // current first codeword is not zero - int Multiplier = StaticTables.IntToExp[Polynomial[Index]]; - - // loop for error correction coofficients - for (int GeneratorIndex = 0; GeneratorIndex < ErrCorrCodewords; GeneratorIndex++) - { - Polynomial[Index + 1 + GeneratorIndex] = (byte)(Polynomial[Index + 1 + GeneratorIndex] ^ StaticTables.ExpToInt[Generator[GeneratorIndex] + Multiplier]); - } - } - } - - private static int Multiply(int Int1, int Int2) - { - return (Int1 == 0 || Int2 == 0) ? 0 : StaticTables.ExpToInt[StaticTables.IntToExp[Int1] + StaticTables.IntToExp[Int2]]; - } - - private static int MultiplyIntByExp(int Int, int Exp) - { - return Int == 0 ? 0 : StaticTables.ExpToInt[StaticTables.IntToExp[Int] + Exp]; - } - - private static int MultiplyDivide(int Int1, int Int2, int Int3) - { - return (Int1 == 0 || Int2 == 0) ? 0 : StaticTables.ExpToInt[(StaticTables.IntToExp[Int1] + StaticTables.IntToExp[Int2] - StaticTables.IntToExp[Int3] + 255) % 255]; - } - - private static int DivideIntByExp(int Int, int Exp) - { - return Int == 0 ? 0 : StaticTables.ExpToInt[StaticTables.IntToExp[Int] - Exp + 255]; - } - - private static void PolynomialMultiply(int[] Result, int[] Poly1, int[] Poly2) - { - Array.Clear(Result, 0, Result.Length); - for (int Index1 = 0; Index1 < Poly1.Length; Index1++) - { - if (Poly1[Index1] == 0) continue; - int loga = StaticTables.IntToExp[Poly1[Index1]]; - int Index2End = Math.Min(Poly2.Length, Result.Length - Index1); - // = Sum(Poly1[Index1] * Poly2[Index2]) for all Index2 - for (int Index2 = 0; Index2 < Index2End; Index2++) - if (Poly2[Index2] != 0) Result[Index1 + Index2] ^= StaticTables.ExpToInt[loga + StaticTables.IntToExp[Poly2[Index2]]]; - } - return; - } - } - - internal class StaticTables - { - // alignment symbols position as function of dimension - internal static readonly byte[][] AlignmentPositionArray = - { - null, - null, - new byte[] { 6, 18}, - new byte[] { 6, 22}, - new byte[] { 6, 26}, - new byte[] { 6, 30}, - new byte[] { 6, 34}, - new byte[] { 6, 22, 38}, - new byte[] { 6, 24, 42}, - new byte[] { 6, 26, 46}, - new byte[] { 6, 28, 50}, - new byte[] { 6, 30, 54}, - new byte[] { 6, 32, 58}, - new byte[] { 6, 34, 62}, - new byte[] { 6, 26, 46, 66}, - new byte[] { 6, 26, 48, 70}, - new byte[] { 6, 26, 50, 74}, - new byte[] { 6, 30, 54, 78}, - new byte[] { 6, 30, 56, 82}, - new byte[] { 6, 30, 58, 86}, - new byte[] { 6, 34, 62, 90}, - new byte[] { 6, 28, 50, 72, 94}, - new byte[] { 6, 26, 50, 74, 98}, - new byte[] { 6, 30, 54, 78, 102}, - new byte[] { 6, 28, 54, 80, 106}, - new byte[] { 6, 32, 58, 84, 110}, - new byte[] { 6, 30, 58, 86, 114}, - new byte[] { 6, 34, 62, 90, 118}, - new byte[] { 6, 26, 50, 74, 98, 122}, - new byte[] { 6, 30, 54, 78, 102, 126}, - new byte[] { 6, 26, 52, 78, 104, 130}, - new byte[] { 6, 30, 56, 82, 108, 134}, - new byte[] { 6, 34, 60, 86, 112, 138}, - new byte[] { 6, 30, 58, 86, 114, 142}, - new byte[] { 6, 34, 62, 90, 118, 146}, - new byte[] { 6, 30, 54, 78, 102, 126, 150}, - new byte[] { 6, 24, 50, 76, 102, 128, 154}, - new byte[] { 6, 28, 54, 80, 106, 132, 158}, - new byte[] { 6, 32, 58, 84, 110, 136, 162}, - new byte[] { 6, 26, 54, 82, 110, 138, 166}, - new byte[] { 6, 30, 58, 86, 114, 142, 170}, - }; - - // maximum code words as function of dimension - internal static readonly int[] MaxCodewordsArray = - { - 0, - 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, - 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, - 1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185, - 2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706 - }; - - // Encodable character set: - // 1) numeric data (digits 0 - 9); - // 2) alphanumeric data (digits 0 - 9; upper case letters A -Z; nine other characters: space, $ % * + - . / : ); - // 3) 8-bit byte data (JIS 8-bit character set (Latin and Kana) in accordance with JIS X 0201); - // 4) Kanji characters (Shift JIS character set in accordance with JIS X 0208 Annex 1 Shift Coded - // Representation. Note that Kanji characters in QR Code can have values 8140HEX -9FFCHEX and E040HEX - - // EBBFHEX , which can be compacted into 13 bits.) - internal static readonly byte[] EncodingTable = - { - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 36, 45, 45, 45, 37, 38, 45, 45, 45, 45, 39, 40, 45, 41, 42, 43, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, 45, 45, 45, 45, 45, - 45, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - }; - - internal static readonly byte[] DecodingTable = - { - (byte) '0', // 0 - (byte) '1', // 1 - (byte) '2', // 2 - (byte) '3', // 3 - (byte) '4', // 4 - (byte) '5', // 5 - (byte) '6', // 6 - (byte) '7', // 7 - (byte) '8', // 8 - (byte) '9', // 9 - (byte) 'A', // 10 - (byte) 'B', // 11 - (byte) 'C', // 12 - (byte) 'D', // 13 - (byte) 'E', // 14 - (byte) 'F', // 15 - (byte) 'G', // 16 - (byte) 'H', // 17 - (byte) 'I', // 18 - (byte) 'J', // 19 - (byte) 'K', // 20 - (byte) 'L', // 21 - (byte) 'M', // 22 - (byte) 'N', // 23 - (byte) 'O', // 24 - (byte) 'P', // 25 - (byte) 'Q', // 26 - (byte) 'R', // 27 - (byte) 'S', // 28 - (byte) 'T', // 29 - (byte) 'U', // 30 - (byte) 'V', // 31 - (byte) 'W', // 32 - (byte) 'X', // 33 - (byte) 'Y', // 34 - (byte) 'Z', // 35 - (byte) ' ', // 36 (space) - (byte) '$', // 37 - (byte) '%', // 38 - (byte) '*', // 39 - (byte) '+', // 40 - (byte) '-', // 41 - (byte) '.', // 42 - (byte) '/', // 43 - (byte) ':', // 44 - }; - - // Error correction block information - // A-Number of blocks in group 1 - internal const int BLOCKS_GROUP1 = 0; - // B-Number of data codewords in blocks of group 1 - internal const int DATA_CODEWORDS_GROUP1 = 1; - // C-Number of blocks in group 2 - internal const int BLOCKS_GROUP2 = 2; - // D-Number of data codewords in blocks of group 2 - internal const int DATA_CODEWORDS_GROUP2 = 3; - - internal static readonly byte[,] ECBlockInfo = - { - // A, B, C, D - { 1, 19, 0, 0}, // 1-L - { 1, 16, 0, 0}, // 1-M - { 1, 13, 0, 0}, // 1-Q - { 1, 9, 0, 0}, // 1-H - { 1, 34, 0, 0}, // 2-L - { 1, 28, 0, 0}, // 2-M - { 1, 22, 0, 0}, // 2-Q - { 1, 16, 0, 0}, // 2-H - { 1, 55, 0, 0}, // 3-L - { 1, 44, 0, 0}, // 3-M - { 2, 17, 0, 0}, // 3-Q - { 2, 13, 0, 0}, // 3-H - { 1, 80, 0, 0}, // 4-L - { 2, 32, 0, 0}, // 4-M - { 2, 24, 0, 0}, // 4-Q - { 4, 9, 0, 0}, // 4-H - { 1, 108, 0, 0}, // 5-L - { 2, 43, 0, 0}, // 5-M - { 2, 15, 2, 16}, // 5-Q - { 2, 11, 2, 12}, // 5-H - { 2, 68, 0, 0}, // 6-L - { 4, 27, 0, 0}, // 6-M - { 4, 19, 0, 0}, // 6-Q - { 4, 15, 0, 0}, // 6-H - { 2, 78, 0, 0}, // 7-L - { 4, 31, 0, 0}, // 7-M - { 2, 14, 4, 15}, // 7-Q - { 4, 13, 1, 14}, // 7-H - { 2, 97, 0, 0}, // 8-L - { 2, 38, 2, 39}, // 8-M - { 4, 18, 2, 19}, // 8-Q - { 4, 14, 2, 15}, // 8-H - { 2, 116, 0, 0}, // 9-L - { 3, 36, 2, 37}, // 9-M - { 4, 16, 4, 17}, // 9-Q - { 4, 12, 4, 13}, // 9-H - { 2, 68, 2, 69}, // 10-L - { 4, 43, 1, 44}, // 10-M - { 6, 19, 2, 20}, // 10-Q - { 6, 15, 2, 16}, // 10-H - { 4, 81, 0, 0}, // 11-L - { 1, 50, 4, 51}, // 11-M - { 4, 22, 4, 23}, // 11-Q - { 3, 12, 8, 13}, // 11-H - { 2, 92, 2, 93}, // 12-L - { 6, 36, 2, 37}, // 12-M - { 4, 20, 6, 21}, // 12-Q - { 7, 14, 4, 15}, // 12-H - { 4, 107, 0, 0}, // 13-L - { 8, 37, 1, 38}, // 13-M - { 8, 20, 4, 21}, // 13-Q - { 12, 11, 4, 12}, // 13-H - { 3, 115, 1, 116}, // 14-L - { 4, 40, 5, 41}, // 14-M - { 11, 16, 5, 17}, // 14-Q - { 11, 12, 5, 13}, // 14-H - { 5, 87, 1, 88}, // 15-L - { 5, 41, 5, 42}, // 15-M - { 5, 24, 7, 25}, // 15-Q - { 11, 12, 7, 13}, // 15-H - { 5, 98, 1, 99}, // 16-L - { 7, 45, 3, 46}, // 16-M - { 15, 19, 2, 20}, // 16-Q - { 3, 15, 13, 16}, // 16-H - { 1, 107, 5, 108}, // 17-L - { 10, 46, 1, 47}, // 17-M - { 1, 22, 15, 23}, // 17-Q - { 2, 14, 17, 15}, // 17-H - { 5, 120, 1, 121}, // 18-L - { 9, 43, 4, 44}, // 18-M - { 17, 22, 1, 23}, // 18-Q - { 2, 14, 19, 15}, // 18-H - { 3, 113, 4, 114}, // 19-L - { 3, 44, 11, 45}, // 19-M - { 17, 21, 4, 22}, // 19-Q - { 9, 13, 16, 14}, // 19-H - { 3, 107, 5, 108}, // 20-L - { 3, 41, 13, 42}, // 20-M - { 15, 24, 5, 25}, // 20-Q - { 15, 15, 10, 16}, // 20-H - { 4, 116, 4, 117}, // 21-L - { 17, 42, 0, 0}, // 21-M - { 17, 22, 6, 23}, // 21-Q - { 19, 16, 6, 17}, // 21-H - { 2, 111, 7, 112}, // 22-L - { 17, 46, 0, 0}, // 22-M - { 7, 24, 16, 25}, // 22-Q - { 34, 13, 0, 0}, // 22-H - { 4, 121, 5, 122}, // 23-L - { 4, 47, 14, 48}, // 23-M - { 11, 24, 14, 25}, // 23-Q - { 16, 15, 14, 16}, // 23-H - { 6, 117, 4, 118}, // 24-L - { 6, 45, 14, 46}, // 24-M - { 11, 24, 16, 25}, // 24-Q - { 30, 16, 2, 17}, // 24-H - { 8, 106, 4, 107}, // 25-L - { 8, 47, 13, 48}, // 25-M - { 7, 24, 22, 25}, // 25-Q - { 22, 15, 13, 16}, // 25-H - { 10, 114, 2, 115}, // 26-L - { 19, 46, 4, 47}, // 26-M - { 28, 22, 6, 23}, // 26-Q - { 33, 16, 4, 17}, // 26-H - { 8, 122, 4, 123}, // 27-L - { 22, 45, 3, 46}, // 27-M - { 8, 23, 26, 24}, // 27-Q - { 12, 15, 28, 16}, // 27-H - { 3, 117, 10, 118}, // 28-L - { 3, 45, 23, 46}, // 28-M - { 4, 24, 31, 25}, // 28-Q - { 11, 15, 31, 16}, // 28-H - { 7, 116, 7, 117}, // 29-L - { 21, 45, 7, 46}, // 29-M - { 1, 23, 37, 24}, // 29-Q - { 19, 15, 26, 16}, // 29-H - { 5, 115, 10, 116}, // 30-L - { 19, 47, 10, 48}, // 30-M - { 15, 24, 25, 25}, // 30-Q - { 23, 15, 25, 16}, // 30-H - { 13, 115, 3, 116}, // 31-L - { 2, 46, 29, 47}, // 31-M - { 42, 24, 1, 25}, // 31-Q - { 23, 15, 28, 16}, // 31-H - { 17, 115, 0, 0}, // 32-L - { 10, 46, 23, 47}, // 32-M - { 10, 24, 35, 25}, // 32-Q - { 19, 15, 35, 16}, // 32-H - { 17, 115, 1, 116}, // 33-L - { 14, 46, 21, 47}, // 33-M - { 29, 24, 19, 25}, // 33-Q - { 11, 15, 46, 16}, // 33-H - { 13, 115, 6, 116}, // 34-L - { 14, 46, 23, 47}, // 34-M - { 44, 24, 7, 25}, // 34-Q - { 59, 16, 1, 17}, // 34-H - { 12, 121, 7, 122}, // 35-L - { 12, 47, 26, 48}, // 35-M - { 39, 24, 14, 25}, // 35-Q - { 22, 15, 41, 16}, // 35-H - { 6, 121, 14, 122}, // 36-L - { 6, 47, 34, 48}, // 36-M - { 46, 24, 10, 25}, // 36-Q - { 2, 15, 64, 16}, // 36-H - { 17, 122, 4, 123}, // 37-L - { 29, 46, 14, 47}, // 37-M - { 49, 24, 10, 25}, // 37-Q - { 24, 15, 46, 16}, // 37-H - { 4, 122, 18, 123}, // 38-L - { 13, 46, 32, 47}, // 38-M - { 48, 24, 14, 25}, // 38-Q - { 42, 15, 32, 16}, // 38-H - { 20, 117, 4, 118}, // 39-L - { 40, 47, 7, 48}, // 39-M - { 43, 24, 22, 25}, // 39-Q - { 10, 15, 67, 16}, // 39-H - { 19, 118, 6, 119}, // 40-L - { 18, 47, 31, 48}, // 40-M - { 34, 24, 34, 25}, // 40-Q - { 20, 15, 61, 16}, // 40-H - }; - - internal static readonly byte[] Generator7 = { 87, 229, 146, 149, 238, 102, 21 }; - internal static readonly byte[] Generator10 = { 251, 67, 46, 61, 118, 70, 64, 94, 32, 45 }; - internal static readonly byte[] Generator13 = { 74, 152, 176, 100, 86, 100, 106, 104, 130, 218, 206, 140, 78 }; - internal static readonly byte[] Generator15 = { 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105 }; - internal static readonly byte[] Generator16 = { 120, 104, 107, 109, 102, 161, 76, 3, 91, 191, 147, 169, 182, 194, 225, 120}; - internal static readonly byte[] Generator17 = { 43, 139, 206, 78, 43, 239, 123, 206, 214, 147, 24, 99, 150, 39, 243, 163, 136}; - internal static readonly byte[] Generator18 = { 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, 98, 96, 153}; - internal static readonly byte[] Generator20 = { 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 164, 212, 212, 188, 190}; - internal static readonly byte[] Generator22 = - { 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, 80, - 219, 134, 160, 105, 165, 231}; - internal static readonly byte[] Generator24 = - { 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, - 111, 0, 117, 232, 87, 96, 227, 21}; - internal static readonly byte[] Generator26 = - { 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, 21, - 245, 142, 13, 102, 48, 227, 153, 145, 218, 70}; - internal static readonly byte[] Generator28 = - { 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 232, 201, - 21, 43, 245, 87, 42, 195, 212, 119, 242, 37, 9, 123}; - internal static readonly byte[] Generator30 = - { 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, - 42, 173, 226, 193, 224, 130, 156, 37, 251, 216, 238, 40, 192, 180}; - internal static readonly byte[] Generator32 = - { 10, 6, 106, 190, 249, 167, 4, 67, 209, 138, 138, 32, 242, 123, 89, 27, - 120, 185, 80, 156, 38, 60, 171, 60, 28, 222, 80, 52, 254, 185, 220, 241}; - internal static readonly byte[] Generator34 = - { 111, 77, 146, 94, 26, 21, 108, 19, 105, 94, 113, 193, 86, 140, 163, 125, - 58, 158, 229, 239, 218, 103, 56, 70, 114, 61, 183, 129, 167, 13, 98, 62, - 129, 51}; - internal static readonly byte[] Generator36 = - { 200, 183, 98, 16, 172, 31, 246, 234, 60, 152, 115, 0, 167, 152, 113, 248, - 238, 107, 18, 63, 218, 37, 87, 210, 105, 177, 120, 74, 121, 196, 117, 251, - 113, 233, 30, 120}; - internal static readonly byte[] Generator40 = - { 59, 116, 79, 161, 252, 98, 128, 205, 128, 161, 247, 57, 163, 56, 235, 106, - 53, 26, 187, 174, 226, 104, 170, 7, 175, 35, 181, 114, 88, 41, 47, 163, - 125, 134, 72, 20, 232, 53, 35, 15}; - internal static readonly byte[] Generator42 = - { 250, 103, 221, 230, 25, 18, 137, 231, 0, 3, 58, 242, 221, 191, 110, 84, - 230, 8, 188, 106, 96, 147, 15, 131, 139, 34, 101, 223, 39, 101, 213, 199, - 237, 254, 201, 123, 171, 162, 194, 117, 50, 96}; - internal static readonly byte[] Generator44 = - { 190, 7, 61, 121, 71, 246, 69, 55, 168, 188, 89, 243, 191, 25, 72, 123, - 9, 145, 14, 247, 1, 238, 44, 78, 143, 62, 224, 126, 118, 114, 68, 163, - 52, 194, 217, 147, 204, 169, 37, 130, 113, 102, 73, 181}; - internal static readonly byte[] Generator46 = - { 112, 94, 88, 112, 253, 224, 202, 115, 187, 99, 89, 5, 54, 113, 129, 44, - 58, 16, 135, 216, 169, 211, 36, 1, 4, 96, 60, 241, 73, 104, 234, 8, - 249, 245, 119, 174, 52, 25, 157, 224, 43, 202, 223, 19, 82, 15}; - internal static readonly byte[] Generator48 = - { 228, 25, 196, 130, 211, 146, 60, 24, 251, 90, 39, 102, 240, 61, 178, 63, - 46, 123, 115, 18, 221, 111, 135, 160, 182, 205, 107, 206, 95, 150, 120, 184, - 91, 21, 247, 156, 140, 238, 191, 11, 94, 227, 84, 50, 163, 39, 34, 108}; - internal static readonly byte[] Generator50 = - { 232, 125, 157, 161, 164, 9, 118, 46, 209, 99, 203, 193, 35, 3, 209, 111, - 195, 242, 203, 225, 46, 13, 32, 160, 126, 209, 130, 160, 242, 215, 242, 75, - 77, 42, 189, 32, 113, 65, 124, 69, 228, 114, 235, 175, 124, 170, 215, 232, - 133, 205}; - internal static readonly byte[] Generator52 = - { 116, 50, 86, 186, 50, 220, 251, 89, 192, 46, 86, 127, 124, 19, 184, 233, - 151, 215, 22, 14, 59, 145, 37, 242, 203, 134, 254, 89, 190, 94, 59, 65, - 124, 113, 100, 233, 235, 121, 22, 76, 86, 97, 39, 242, 200, 220, 101, 33, - 239, 254, 116, 51}; - internal static readonly byte[] Generator54 = - { 183, 26, 201, 84, 210, 221, 113, 21, 46, 65, 45, 50, 238, 184, 249, 225, - 102, 58, 209, 218, 109, 165, 26, 95, 184, 192, 52, 245, 35, 254, 238, 175, - 172, 79, 123, 25, 122, 43, 120, 108, 215, 80, 128, 201, 235, 8, 153, 59, - 101, 31, 198, 76, 31, 156}; - internal static readonly byte[] Generator56 = - { 106, 120, 107, 157, 164, 216, 112, 116, 2, 91, 248, 163, 36, 201, 202, 229, - 6, 144, 254, 155, 135, 208, 170, 209, 12, 139, 127, 142, 182, 249, 177, 174, - 190, 28, 10, 85, 239, 184, 101, 124, 152, 206, 96, 23, 163, 61, 27, 196, - 247, 151, 154, 202, 207, 20, 61, 10}; - internal static readonly byte[] Generator58 = - { 82, 116, 26, 247, 66, 27, 62, 107, 252, 182, 200, 185, 235, 55, 251, 242, - 210, 144, 154, 237, 176, 141, 192, 248, 152, 249, 206, 85, 253, 142, 65, 165, - 125, 23, 24, 30, 122, 240, 214, 6, 129, 218, 29, 145, 127, 134, 206, 245, - 117, 29, 41, 63, 159, 142, 233, 125, 148, 123}; - internal static readonly byte[] Generator60 = - { 107, 140, 26, 12, 9, 141, 243, 197, 226, 197, 219, 45, 211, 101, 219, 120, - 28, 181, 127, 6, 100, 247, 2, 205, 198, 57, 115, 219, 101, 109, 160, 82, - 37, 38, 238, 49, 160, 209, 121, 86, 11, 124, 30, 181, 84, 25, 194, 87, - 65, 102, 190, 220, 70, 27, 209, 16, 89, 7, 33, 240}; - internal static readonly byte[] Generator62 = - { 65, 202, 113, 98, 71, 223, 248, 118, 214, 94, 0, 122, 37, 23, 2, 228, - 58, 121, 7, 105, 135, 78, 243, 118, 70, 76, 223, 89, 72, 50, 70, 111, - 194, 17, 212, 126, 181, 35, 221, 117, 235, 11, 229, 149, 147, 123, 213, 40, - 115, 6, 200, 100, 26, 246, 182, 218, 127, 215, 36, 186, 110, 106}; - internal static readonly byte[] Generator64 = - { 45, 51, 175, 9, 7, 158, 159, 49, 68, 119, 92, 123, 177, 204, 187, 254, - 200, 78, 141, 149, 119, 26, 127, 53, 160, 93, 199, 212, 29, 24, 145, 156, - 208, 150, 218, 209, 4, 216, 91, 47, 184, 146, 47, 140, 195, 195, 125, 242, - 238, 63, 99, 108, 140, 230, 242, 31, 204, 11, 178, 243, 217, 156, 213, 231}; - internal static readonly byte[] Generator66 = - { 5, 118, 222, 180, 136, 136, 162, 51, 46, 117, 13, 215, 81, 17, 139, 247, - 197, 171, 95, 173, 65, 137, 178, 68, 111, 95, 101, 41, 72, 214, 169, 197, - 95, 7, 44, 154, 77, 111, 236, 40, 121, 143, 63, 87, 80, 253, 240, 126, - 217, 77, 34, 232, 106, 50, 168, 82, 76, 146, 67, 106, 171, 25, 132, 93, - 45, 105}; - internal static readonly byte[] Generator68 = - { 247, 159, 223, 33, 224, 93, 77, 70, 90, 160, 32, 254, 43, 150, 84, 101, - 190, 205, 133, 52, 60, 202, 165, 220, 203, 151, 93, 84, 15, 84, 253, 173, - 160, 89, 227, 52, 199, 97, 95, 231, 52, 177, 41, 125, 137, 241, 166, 225, - 118, 2, 54, 32, 82, 215, 175, 198, 43, 238, 235, 27, 101, 184, 127, 3, - 5, 8, 163, 238}; - - internal static readonly byte[][] GenArray = - { Generator7, null, null, Generator10, null, null, Generator13, null, Generator15, Generator16, - Generator17, Generator18, null, Generator20, null, Generator22, null, Generator24, null, Generator26, - null, Generator28, null, Generator30, null, Generator32, null, Generator34, null, Generator36, - null, null, null, Generator40, null, Generator42, null, Generator44, null, Generator46, - null, Generator48, null, Generator50, null, Generator52, null, Generator54, null, Generator56, - null, Generator58, null, Generator60, null, Generator62, null, Generator64, null, Generator66, - null, Generator68}; - - internal static readonly byte[] ExpToInt = // ExpToInt = - { - 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, - 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, - 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, - 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, - 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, - 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, - 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, - 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, - 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, - 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, - 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, - 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, - 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, - 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, - 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, - 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1, - - 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, - 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, - 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, - 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, - 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, - 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, - 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, - 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, - 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, - 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, - 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, - 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, - 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, - 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, - 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, - 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1 - }; - - internal static readonly byte[] IntToExp = // IntToExp = - { - 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, - 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, - 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, - 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, - 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, - 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, - 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, - 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, - 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, - 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, - 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, - 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, - 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, - 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, - 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, - 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 - }; - - internal static readonly int[] FormatInfoArray = - { - 0x5412, 0x5125, 0x5E7C, 0x5B4B, 0x45F9, 0x40CE, 0x4F97, 0x4AA0, // M = 00 - 0x77C4, 0x72F3, 0x7DAA, 0x789D, 0x662F, 0x6318, 0x6C41, 0x6976, // L = 01 - 0x1689, 0x13BE, 0x1CE7, 0x19D0, 0x762, 0x255, 0xD0C, 0x83B, // H - 10 - 0x355F, 0x3068, 0x3F31, 0x3A06, 0x24B4, 0x2183, 0x2EDA, 0x2BED, // Q = 11 - }; - - internal static readonly int[,] FormatInfoOne = new int[,] - { - {0, 8}, {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {7, 8}, {8, 8}, - {8, 7}, {8, 5}, {8, 4}, {8, 3}, {8, 2}, {8, 1}, {8, 0} - }; - - internal static readonly int[,] FormatInfoTwo = new int[,] - { - {8, -1}, {8, -2}, {8, -3}, {8, -4}, {8, -5}, {8, -6}, {8, -7}, {8, -8}, - {-7, 8}, {-6, 8}, {-5, 8}, {-4, 8}, {-3, 8}, {-2, 8}, {-1, 8} - }; - - internal static readonly int[] VersionCodeArray = - { - 0x7c94, 0x85bc, 0x9a99, 0xa4d3, 0xbbf6, 0xc762, 0xd847, 0xe60d, 0xf928, 0x10b78, - 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, - 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, - 0x2542e, 0x26a64, 0x27541, 0x28c69 - }; - - internal const byte White = 0; - internal const byte Black = 1; - internal const byte NonData = 2; - internal const byte Fixed = 4; - internal const byte DataWhite = White; - internal const byte DataBlack = Black; - internal const byte FormatWhite = NonData | White; - internal const byte FormatBlack = NonData | Black; - internal const byte FixedWhite = Fixed | NonData | White; - internal const byte FixedBlack = Fixed | NonData | Black; - - internal static readonly byte[,] FinderPatternTopLeft = - { - {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FormatWhite}, - {FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FormatWhite}, - {FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite}, - }; - - internal static readonly byte[,] FinderPatternTopRight = - { - {FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack}, - {FixedWhite, FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, - {FixedWhite, FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack}, - {FixedWhite, FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack}, - {FixedWhite, FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack}, - {FixedWhite, FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, - {FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack}, - {FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite}, - {FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite}, - }; - - internal static readonly byte[,] FinderPatternBottomLeft = - { - {FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, - {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, - {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FormatWhite}, - }; - - internal static readonly byte[,] AlignmentPattern = - { - {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack}, - {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, - {FixedBlack, FixedWhite, FixedBlack, FixedWhite, FixedBlack}, - {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, - {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack}, - }; - } -} \ No newline at end of file diff --git a/src/QRCode/QRCodeGenerator.cs b/src/QRCode/QRCodeGenerator.cs deleted file mode 100644 index da048f2..0000000 --- a/src/QRCode/QRCodeGenerator.cs +++ /dev/null @@ -1,1626 +0,0 @@ -/* -This file was taken from https://github.com/codebude/QRCoder and is licensed under the MIT license. ---------------------- -The MIT License (MIT) - -Copyright (c) 2013-2015 Raffael Herrmann - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Collections; - -namespace QRCoder -{ - public class QRCodeGenerator : IDisposable - { - private static readonly char[] alphanumEncTable = { ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; - private static readonly int[] capacityBaseValues = { 41, 25, 17, 10, 34, 20, 14, 8, 27, 16, 11, 7, 17, 10, 7, 4, 77, 47, 32, 20, 63, 38, 26, 16, 48, 29, 20, 12, 34, 20, 14, 8, 127, 77, 53, 32, 101, 61, 42, 26, 77, 47, 32, 20, 58, 35, 24, 15, 187, 114, 78, 48, 149, 90, 62, 38, 111, 67, 46, 28, 82, 50, 34, 21, 255, 154, 106, 65, 202, 122, 84, 52, 144, 87, 60, 37, 106, 64, 44, 27, 322, 195, 134, 82, 255, 154, 106, 65, 178, 108, 74, 45, 139, 84, 58, 36, 370, 224, 154, 95, 293, 178, 122, 75, 207, 125, 86, 53, 154, 93, 64, 39, 461, 279, 192, 118, 365, 221, 152, 93, 259, 157, 108, 66, 202, 122, 84, 52, 552, 335, 230, 141, 432, 262, 180, 111, 312, 189, 130, 80, 235, 143, 98, 60, 652, 395, 271, 167, 513, 311, 213, 131, 364, 221, 151, 93, 288, 174, 119, 74, 772, 468, 321, 198, 604, 366, 251, 155, 427, 259, 177, 109, 331, 200, 137, 85, 883, 535, 367, 226, 691, 419, 287, 177, 489, 296, 203, 125, 374, 227, 155, 96, 1022, 619, 425, 262, 796, 483, 331, 204, 580, 352, 241, 149, 427, 259, 177, 109, 1101, 667, 458, 282, 871, 528, 362, 223, 621, 376, 258, 159, 468, 283, 194, 120, 1250, 758, 520, 320, 991, 600, 412, 254, 703, 426, 292, 180, 530, 321, 220, 136, 1408, 854, 586, 361, 1082, 656, 450, 277, 775, 470, 322, 198, 602, 365, 250, 154, 1548, 938, 644, 397, 1212, 734, 504, 310, 876, 531, 364, 224, 674, 408, 280, 173, 1725, 1046, 718, 442, 1346, 816, 560, 345, 948, 574, 394, 243, 746, 452, 310, 191, 1903, 1153, 792, 488, 1500, 909, 624, 384, 1063, 644, 442, 272, 813, 493, 338, 208, 2061, 1249, 858, 528, 1600, 970, 666, 410, 1159, 702, 482, 297, 919, 557, 382, 235, 2232, 1352, 929, 572, 1708, 1035, 711, 438, 1224, 742, 509, 314, 969, 587, 403, 248, 2409, 1460, 1003, 618, 1872, 1134, 779, 480, 1358, 823, 565, 348, 1056, 640, 439, 270, 2620, 1588, 1091, 672, 2059, 1248, 857, 528, 1468, 890, 611, 376, 1108, 672, 461, 284, 2812, 1704, 1171, 721, 2188, 1326, 911, 561, 1588, 963, 661, 407, 1228, 744, 511, 315, 3057, 1853, 1273, 784, 2395, 1451, 997, 614, 1718, 1041, 715, 440, 1286, 779, 535, 330, 3283, 1990, 1367, 842, 2544, 1542, 1059, 652, 1804, 1094, 751, 462, 1425, 864, 593, 365, 3517, 2132, 1465, 902, 2701, 1637, 1125, 692, 1933, 1172, 805, 496, 1501, 910, 625, 385, 3669, 2223, 1528, 940, 2857, 1732, 1190, 732, 2085, 1263, 868, 534, 1581, 958, 658, 405, 3909, 2369, 1628, 1002, 3035, 1839, 1264, 778, 2181, 1322, 908, 559, 1677, 1016, 698, 430, 4158, 2520, 1732, 1066, 3289, 1994, 1370, 843, 2358, 1429, 982, 604, 1782, 1080, 742, 457, 4417, 2677, 1840, 1132, 3486, 2113, 1452, 894, 2473, 1499, 1030, 634, 1897, 1150, 790, 486, 4686, 2840, 1952, 1201, 3693, 2238, 1538, 947, 2670, 1618, 1112, 684, 2022, 1226, 842, 518, 4965, 3009, 2068, 1273, 3909, 2369, 1628, 1002, 2805, 1700, 1168, 719, 2157, 1307, 898, 553, 5253, 3183, 2188, 1347, 4134, 2506, 1722, 1060, 2949, 1787, 1228, 756, 2301, 1394, 958, 590, 5529, 3351, 2303, 1417, 4343, 2632, 1809, 1113, 3081, 1867, 1283, 790, 2361, 1431, 983, 605, 5836, 3537, 2431, 1496, 4588, 2780, 1911, 1176, 3244, 1966, 1351, 832, 2524, 1530, 1051, 647, 6153, 3729, 2563, 1577, 4775, 2894, 1989, 1224, 3417, 2071, 1423, 876, 2625, 1591, 1093, 673, 6479, 3927, 2699, 1661, 5039, 3054, 2099, 1292, 3599, 2181, 1499, 923, 2735, 1658, 1139, 701, 6743, 4087, 2809, 1729, 5313, 3220, 2213, 1362, 3791, 2298, 1579, 972, 2927, 1774, 1219, 750, 7089, 4296, 2953, 1817, 5596, 3391, 2331, 1435, 3993, 2420, 1663, 1024, 3057, 1852, 1273, 784 }; - private static readonly int[] capacityECCBaseValues = { 19, 7, 1, 19, 0, 0, 16, 10, 1, 16, 0, 0, 13, 13, 1, 13, 0, 0, 9, 17, 1, 9, 0, 0, 34, 10, 1, 34, 0, 0, 28, 16, 1, 28, 0, 0, 22, 22, 1, 22, 0, 0, 16, 28, 1, 16, 0, 0, 55, 15, 1, 55, 0, 0, 44, 26, 1, 44, 0, 0, 34, 18, 2, 17, 0, 0, 26, 22, 2, 13, 0, 0, 80, 20, 1, 80, 0, 0, 64, 18, 2, 32, 0, 0, 48, 26, 2, 24, 0, 0, 36, 16, 4, 9, 0, 0, 108, 26, 1, 108, 0, 0, 86, 24, 2, 43, 0, 0, 62, 18, 2, 15, 2, 16, 46, 22, 2, 11, 2, 12, 136, 18, 2, 68, 0, 0, 108, 16, 4, 27, 0, 0, 76, 24, 4, 19, 0, 0, 60, 28, 4, 15, 0, 0, 156, 20, 2, 78, 0, 0, 124, 18, 4, 31, 0, 0, 88, 18, 2, 14, 4, 15, 66, 26, 4, 13, 1, 14, 194, 24, 2, 97, 0, 0, 154, 22, 2, 38, 2, 39, 110, 22, 4, 18, 2, 19, 86, 26, 4, 14, 2, 15, 232, 30, 2, 116, 0, 0, 182, 22, 3, 36, 2, 37, 132, 20, 4, 16, 4, 17, 100, 24, 4, 12, 4, 13, 274, 18, 2, 68, 2, 69, 216, 26, 4, 43, 1, 44, 154, 24, 6, 19, 2, 20, 122, 28, 6, 15, 2, 16, 324, 20, 4, 81, 0, 0, 254, 30, 1, 50, 4, 51, 180, 28, 4, 22, 4, 23, 140, 24, 3, 12, 8, 13, 370, 24, 2, 92, 2, 93, 290, 22, 6, 36, 2, 37, 206, 26, 4, 20, 6, 21, 158, 28, 7, 14, 4, 15, 428, 26, 4, 107, 0, 0, 334, 22, 8, 37, 1, 38, 244, 24, 8, 20, 4, 21, 180, 22, 12, 11, 4, 12, 461, 30, 3, 115, 1, 116, 365, 24, 4, 40, 5, 41, 261, 20, 11, 16, 5, 17, 197, 24, 11, 12, 5, 13, 523, 22, 5, 87, 1, 88, 415, 24, 5, 41, 5, 42, 295, 30, 5, 24, 7, 25, 223, 24, 11, 12, 7, 13, 589, 24, 5, 98, 1, 99, 453, 28, 7, 45, 3, 46, 325, 24, 15, 19, 2, 20, 253, 30, 3, 15, 13, 16, 647, 28, 1, 107, 5, 108, 507, 28, 10, 46, 1, 47, 367, 28, 1, 22, 15, 23, 283, 28, 2, 14, 17, 15, 721, 30, 5, 120, 1, 121, 563, 26, 9, 43, 4, 44, 397, 28, 17, 22, 1, 23, 313, 28, 2, 14, 19, 15, 795, 28, 3, 113, 4, 114, 627, 26, 3, 44, 11, 45, 445, 26, 17, 21, 4, 22, 341, 26, 9, 13, 16, 14, 861, 28, 3, 107, 5, 108, 669, 26, 3, 41, 13, 42, 485, 30, 15, 24, 5, 25, 385, 28, 15, 15, 10, 16, 932, 28, 4, 116, 4, 117, 714, 26, 17, 42, 0, 0, 512, 28, 17, 22, 6, 23, 406, 30, 19, 16, 6, 17, 1006, 28, 2, 111, 7, 112, 782, 28, 17, 46, 0, 0, 568, 30, 7, 24, 16, 25, 442, 24, 34, 13, 0, 0, 1094, 30, 4, 121, 5, 122, 860, 28, 4, 47, 14, 48, 614, 30, 11, 24, 14, 25, 464, 30, 16, 15, 14, 16, 1174, 30, 6, 117, 4, 118, 914, 28, 6, 45, 14, 46, 664, 30, 11, 24, 16, 25, 514, 30, 30, 16, 2, 17, 1276, 26, 8, 106, 4, 107, 1000, 28, 8, 47, 13, 48, 718, 30, 7, 24, 22, 25, 538, 30, 22, 15, 13, 16, 1370, 28, 10, 114, 2, 115, 1062, 28, 19, 46, 4, 47, 754, 28, 28, 22, 6, 23, 596, 30, 33, 16, 4, 17, 1468, 30, 8, 122, 4, 123, 1128, 28, 22, 45, 3, 46, 808, 30, 8, 23, 26, 24, 628, 30, 12, 15, 28, 16, 1531, 30, 3, 117, 10, 118, 1193, 28, 3, 45, 23, 46, 871, 30, 4, 24, 31, 25, 661, 30, 11, 15, 31, 16, 1631, 30, 7, 116, 7, 117, 1267, 28, 21, 45, 7, 46, 911, 30, 1, 23, 37, 24, 701, 30, 19, 15, 26, 16, 1735, 30, 5, 115, 10, 116, 1373, 28, 19, 47, 10, 48, 985, 30, 15, 24, 25, 25, 745, 30, 23, 15, 25, 16, 1843, 30, 13, 115, 3, 116, 1455, 28, 2, 46, 29, 47, 1033, 30, 42, 24, 1, 25, 793, 30, 23, 15, 28, 16, 1955, 30, 17, 115, 0, 0, 1541, 28, 10, 46, 23, 47, 1115, 30, 10, 24, 35, 25, 845, 30, 19, 15, 35, 16, 2071, 30, 17, 115, 1, 116, 1631, 28, 14, 46, 21, 47, 1171, 30, 29, 24, 19, 25, 901, 30, 11, 15, 46, 16, 2191, 30, 13, 115, 6, 116, 1725, 28, 14, 46, 23, 47, 1231, 30, 44, 24, 7, 25, 961, 30, 59, 16, 1, 17, 2306, 30, 12, 121, 7, 122, 1812, 28, 12, 47, 26, 48, 1286, 30, 39, 24, 14, 25, 986, 30, 22, 15, 41, 16, 2434, 30, 6, 121, 14, 122, 1914, 28, 6, 47, 34, 48, 1354, 30, 46, 24, 10, 25, 1054, 30, 2, 15, 64, 16, 2566, 30, 17, 122, 4, 123, 1992, 28, 29, 46, 14, 47, 1426, 30, 49, 24, 10, 25, 1096, 30, 24, 15, 46, 16, 2702, 30, 4, 122, 18, 123, 2102, 28, 13, 46, 32, 47, 1502, 30, 48, 24, 14, 25, 1142, 30, 42, 15, 32, 16, 2812, 30, 20, 117, 4, 118, 2216, 28, 40, 47, 7, 48, 1582, 30, 43, 24, 22, 25, 1222, 30, 10, 15, 67, 16, 2956, 30, 19, 118, 6, 119, 2334, 28, 18, 47, 31, 48, 1666, 30, 34, 24, 34, 25, 1276, 30, 20, 15, 61, 16 }; - private static readonly int[] alignmentPatternBaseValues = { 0, 0, 0, 0, 0, 0, 0, 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, 0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, 0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, 80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, 82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, 158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 }; - private static readonly int[] remainderBits = { 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 }; - - private static readonly List alignmentPatternTable = CreateAlignmentPatternTable(); - private static readonly List capacityECCTable = CreateCapacityECCTable(); - private static readonly List capacityTable = CreateCapacityTable(); - private static readonly List galoisField = CreateAntilogTable(); - private static readonly Dictionary alphanumEncDict = CreateAlphanumEncDict(); - - public enum EciMode - { - Default = 0, - Iso8859_1 = 3, - Iso8859_2 = 4, - Utf8 = 26 - } - - /// - /// Initializes the QR code generator - /// - public QRCodeGenerator() - { - } - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A payload object, generated by the PayloadGenerator-class - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - /* - public QRCodeData CreateQrCode(PayloadGenerator.Payload payload) - { - return GenerateQrCode(payload); - } - */ - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A payload object, generated by the PayloadGenerator-class - /// The level of error correction data - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - /* - public QRCodeData CreateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel) - { - return GenerateQrCode(payload, eccLevel); - } - */ - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// The payload which shall be encoded in the QR code - /// The level of error correction data - /// Shall the generator be forced to work in UTF-8 mode? - /// Should the byte-order-mark be used? - /// Which ECI mode shall be used? - /// Set fixed QR code target version. - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public QRCodeData CreateQrCode(string plainText, ECCLevel eccLevel) - { - return GenerateQrCode(plainText, eccLevel, false, false, QRCodeGenerator.EciMode.Default, -1); - } - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A byte array which shall be encoded/stored in the QR code - /// The level of error correction data - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - /* - public QRCodeData CreateQrCode(byte[] binaryData, ECCLevel eccLevel) - { - return GenerateQrCode(binaryData, eccLevel); - } - */ - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A payload object, generated by the PayloadGenerator-class - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - /* - public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload) - { - return GenerateQrCode(payload.ToString(), payload.EccLevel, false, false, payload.EciMode, payload.Version); - } - */ - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A payload object, generated by the PayloadGenerator-class - /// The level of error correction data - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - /* - public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel) - { - return GenerateQrCode(payload.ToString(), eccLevel, false, false, payload.EciMode, payload.Version); - } - */ - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// The payload which shall be encoded in the QR code - /// The level of error correction data - /// Shall the generator be forced to work in UTF-8 mode? - /// Should the byte-order-mark be used? - /// Which ECI mode shall be used? - /// Set fixed QR code target version. - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - - public static QRCodeData GenerateQrCode(string plainText) - { - return GenerateQrCode(plainText, ECCLevel.Q, true, false, EciMode.Default, -1); - } - - public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, bool forceUtf8, bool utf8BOM, EciMode eciMode, int requestedVersion) - { - EncodingMode encoding = EncodingMode.Byte; //GetEncodingFromPlaintext(plainText, forceUtf8); - var codedText = PlainTextToBinaryByte(plainText, eciMode, utf8BOM, forceUtf8); - var dataInputLength = GetDataLength(encoding, plainText, codedText, forceUtf8); - int version = requestedVersion; - if (version == -1) - { - version = GetVersion(dataInputLength, encoding, eccLevel); - } - - string modeIndicator = String.Empty; - if (eciMode != EciMode.Default) - { - modeIndicator = DecToBin((int)EncodingMode.ECI, 4); - modeIndicator += DecToBin((int)eciMode, 8); - } - modeIndicator += DecToBin((int)encoding, 4); - var countIndicator = DecToBin(dataInputLength, GetCountIndicatorLength(version, encoding)); - var bitString = modeIndicator + countIndicator; - - bitString += codedText; - - return GenerateQrCode(bitString, eccLevel, version); - } - - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A byte array which shall be encoded/stored in the QR code - /// The level of error correction data - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) - { - int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel); - - string modeIndicator = DecToBin((int)EncodingMode.Byte, 4); - string countIndicator = DecToBin(binaryData.Length, GetCountIndicatorLength(version, EncodingMode.Byte)); - - string bitString = modeIndicator + countIndicator; - foreach (byte b in binaryData) - { - bitString += DecToBin(b, 8); - } - - return GenerateQrCode(bitString, eccLevel, version); - } - - private static QRCodeData GenerateQrCode(string bitString, ECCLevel eccLevel, int version) - { - //Fill up data code word - var eccInfo = capacityECCTable.Single(x => x.Version == version && x.ErrorCorrectionLevel == eccLevel); - var dataLength = eccInfo.TotalDataCodewords * 8; - var lengthDiff = dataLength - bitString.Length; - if (lengthDiff > 0) - bitString += new string('0', Math.Min(lengthDiff, 4)); - if ((bitString.Length % 8) != 0) - bitString += new string('0', 8 - (bitString.Length % 8)); - while (bitString.Length < dataLength) - bitString += "1110110000010001"; - if (bitString.Length > dataLength) - bitString = bitString.Substring(0, dataLength); - - //Calculate error correction words - var codeWordWithECC = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); - for (var i = 0; i < eccInfo.BlocksInGroup1; i++) - { - var bitStr = bitString.Substring(i * eccInfo.CodewordsInGroup1 * 8, eccInfo.CodewordsInGroup1 * 8); - var bitBlockList = BinaryStringToBitBlockList(bitStr); - var bitBlockListDec = BinaryStringListToDecList(bitBlockList); - var eccWordList = CalculateECCWords(bitStr, eccInfo); - var eccWordListDec = BinaryStringListToDecList(eccWordList); - codeWordWithECC.Add( - new CodewordBlock(1, - i + 1, - bitStr, - bitBlockList, - eccWordList, - bitBlockListDec, - eccWordListDec) - ); - } - bitString = bitString.Substring(eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8); - for (var i = 0; i < eccInfo.BlocksInGroup2; i++) - { - var bitStr = bitString.Substring(i * eccInfo.CodewordsInGroup2 * 8, eccInfo.CodewordsInGroup2 * 8); - var bitBlockList = BinaryStringToBitBlockList(bitStr); - var bitBlockListDec = BinaryStringListToDecList(bitBlockList); - var eccWordList = CalculateECCWords(bitStr, eccInfo); - var eccWordListDec = BinaryStringListToDecList(eccWordList); - codeWordWithECC.Add(new CodewordBlock(2, - i + 1, - bitStr, - bitBlockList, - eccWordList, - bitBlockListDec, - eccWordListDec) - ); - } - - - //Interleave code words - var interleavedWordsSb = new StringBuilder(); - for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) - { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.CodeWords.Count > i) - interleavedWordsSb.Append(codeBlock.CodeWords[i]); - } - - - for (var i = 0; i < eccInfo.ECCPerBlock; i++) - { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.ECCWords.Count > i) - interleavedWordsSb.Append(codeBlock.ECCWords[i]); - } - interleavedWordsSb.Append(new string('0', remainderBits[version - 1])); - var interleavedData = interleavedWordsSb.ToString(); - - - //Place interleaved data on module matrix - var qr = new QRCodeData(version); - var blockedModules = new List(); - ModulePlacer.PlaceFinderPatterns(ref qr, ref blockedModules); - ModulePlacer.ReserveSeperatorAreas(qr.ModuleMatrix.Count, ref blockedModules); - ModulePlacer.PlaceAlignmentPatterns(ref qr, alignmentPatternTable.Where(x => x.Version == version).Select(x => x.PatternPositions).First(), ref blockedModules); - ModulePlacer.PlaceTimingPatterns(ref qr, ref blockedModules); - ModulePlacer.PlaceDarkModule(ref qr, version, ref blockedModules); - ModulePlacer.ReserveVersionAreas(qr.ModuleMatrix.Count, version, ref blockedModules); - ModulePlacer.PlaceDataWords(ref qr, interleavedData, ref blockedModules); - var maskVersion = ModulePlacer.MaskCode(ref qr, version, ref blockedModules, eccLevel); - var formatStr = GetFormatString(eccLevel, maskVersion); - - ModulePlacer.PlaceFormat(ref qr, formatStr); - if (version >= 7) - { - var versionString = GetVersionString(version); - ModulePlacer.PlaceVersion(ref qr, versionString); - } - - - ModulePlacer.AddQuietZone(ref qr); - return qr; - } - - private static string GetFormatString(ECCLevel level, int maskVersion) - { - var generator = "10100110111"; - var fStrMask = "101010000010010"; - - var fStr = (level == ECCLevel.L) ? "01" : (level == ECCLevel.M) ? "00" : (level == ECCLevel.Q) ? "11" : "10"; - fStr += DecToBin(maskVersion, 3); - var fStrEcc = fStr.PadRight(15, '0').TrimStart('0'); - while (fStrEcc.Length > 10) - { - var sb = new StringBuilder(); - generator = generator.PadRight(fStrEcc.Length, '0'); - for (var i = 0; i < fStrEcc.Length; i++) - sb.Append((Convert.ToInt32(fStrEcc[i]) ^ Convert.ToInt32(generator[i])).ToString()); - fStrEcc = sb.ToString().TrimStart('0'); - } - fStrEcc = fStrEcc.PadLeft(10, '0'); - fStr += fStrEcc; - - var sbMask = new StringBuilder(); - for (var i = 0; i < fStr.Length; i++) - sbMask.Append((Convert.ToInt32(fStr[i]) ^ Convert.ToInt32(fStrMask[i])).ToString()); - return sbMask.ToString(); - } - - private static string GetVersionString(int version) - { - var generator = "1111100100101"; - - var vStr = DecToBin(version, 6); - var vStrEcc = vStr.PadRight(18, '0').TrimStart('0'); - while (vStrEcc.Length > 12) - { - var sb = new StringBuilder(); - generator = generator.PadRight(vStrEcc.Length, '0'); - for (var i = 0; i < vStrEcc.Length; i++) - sb.Append((Convert.ToInt32(vStrEcc[i]) ^ Convert.ToInt32(generator[i])).ToString()); - vStrEcc = sb.ToString().TrimStart('0'); - } - vStrEcc = vStrEcc.PadLeft(12, '0'); - vStr += vStrEcc; - - return vStr; - } - - private static class ModulePlacer - { - public static void AddQuietZone(ref QRCodeData qrCode) - { - var quietLine = new bool[qrCode.ModuleMatrix.Count + 8]; - for (var i = 0; i < quietLine.Length; i++) - quietLine[i] = false; - for (var i = 0; i < 4; i++) - qrCode.ModuleMatrix.Insert(0, new BitArray(quietLine)); - for (var i = 0; i < 4; i++) - qrCode.ModuleMatrix.Add(new BitArray(quietLine)); - for (var i = 4; i < qrCode.ModuleMatrix.Count - 4; i++) - { - bool[] quietPart = { false, false, false, false }; - var tmpLine = new List(quietPart); - tmpLine.AddRange(qrCode.ModuleMatrix[i].Cast()); - tmpLine.AddRange(quietPart); - qrCode.ModuleMatrix[i] = new BitArray(tmpLine.ToArray()); - } - } - - private static string ReverseString(string inp) - { - string newStr = string.Empty; - if (inp.Length > 0) - { - for (int i = inp.Length - 1; i >= 0; i--) - newStr += inp[i]; - } - return newStr; - } - - public static void PlaceVersion(ref QRCodeData qrCode, string versionStr) - { - var size = qrCode.ModuleMatrix.Count; - - var vStr = ReverseString(versionStr); - - for (var x = 0; x < 6; x++) - { - for (var y = 0; y < 3; y++) - { - qrCode.ModuleMatrix[y + size - 11][x] = vStr[x * 3 + y] == '1'; - qrCode.ModuleMatrix[x][y + size - 11] = vStr[x * 3 + y] == '1'; - } - } - } - - public static void PlaceFormat(ref QRCodeData qrCode, string formatStr) - { - var size = qrCode.ModuleMatrix.Count; - var fStr = ReverseString(formatStr); - var modules = new[,] { - { 8, 0, size - 1, 8 }, - { 8, 1, size - 2, 8 }, - { 8, 2, size - 3, 8 }, - { 8, 3, size - 4, 8 }, - { 8, 4, size - 5, 8 }, - { 8, 5, size - 6, 8 }, - { 8, 7, size - 7, 8 }, - { 8, 8, size - 8, 8 }, - { 7, 8, 8, size - 7 }, - { 5, 8, 8, size - 6 }, - { 4, 8, 8, size - 5 }, - { 3, 8, 8, size - 4 }, - { 2, 8, 8, size - 3 }, - { 1, 8, 8, size - 2 }, - { 0, 8, 8, size - 1 } }; - for (var i = 0; i < 15; i++) - { - var p1 = new Point(modules[i, 0], modules[i, 1]); - var p2 = new Point(modules[i, 2], modules[i, 3]); - qrCode.ModuleMatrix[p1.Y][p1.X] = fStr[i] == '1'; - qrCode.ModuleMatrix[p2.Y][p2.X] = fStr[i] == '1'; - } - } - - - public static int MaskCode(ref QRCodeData qrCode, int version, ref List blockedModules, ECCLevel eccLevel) - { - int? selectedPattern = null; - var patternScore = 0; - - var size = qrCode.ModuleMatrix.Count; - - var methods = new Dictionary>(8) { - { 1, MaskPattern.Pattern1 }, {2, MaskPattern.Pattern2 }, {3, MaskPattern.Pattern3 }, {4, MaskPattern.Pattern4 }, - {5, MaskPattern.Pattern5 }, {6, MaskPattern.Pattern6 }, {7, MaskPattern.Pattern7 }, {8, MaskPattern.Pattern8 } - }; - - foreach (var pattern in methods) - { - var qrTemp = new QRCodeData(version); - for (var y = 0; y < size; y++) - { - for (var x = 0; x < size; x++) - { - qrTemp.ModuleMatrix[y][x] = qrCode.ModuleMatrix[y][x]; - } - - } - - var formatStr = GetFormatString(eccLevel, pattern.Key - 1); - ModulePlacer.PlaceFormat(ref qrTemp, formatStr); - if (version >= 7) - { - var versionString = GetVersionString(version); - ModulePlacer.PlaceVersion(ref qrTemp, versionString); - } - - for (var x = 0; x < size; x++) - { - for (var y = 0; y < x; y++) - { - if (!IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) - { - qrTemp.ModuleMatrix[y][x] ^= pattern.Value(x, y); - qrTemp.ModuleMatrix[x][y] ^= pattern.Value(y, x); - } - } - - if (!IsBlocked(new Rectangle(x, x, 1, 1), blockedModules)) - { - qrTemp.ModuleMatrix[x][x] ^= pattern.Value(x, x); - } - } - - var score = MaskPattern.Score(ref qrTemp); - if (!selectedPattern.HasValue || patternScore > score) - { - selectedPattern = pattern.Key; - patternScore = score; - } - } - - for (var x = 0; x < size; x++) - { - for (var y = 0; y < x; y++) - { - if (!IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) - { - qrCode.ModuleMatrix[y][x] ^= methods[selectedPattern.Value](x, y); - qrCode.ModuleMatrix[x][y] ^= methods[selectedPattern.Value](y, x); - } - } - - if (!IsBlocked(new Rectangle(x, x, 1, 1), blockedModules)) - { - qrCode.ModuleMatrix[x][x] ^= methods[selectedPattern.Value](x, x); - } - } - return selectedPattern.Value - 1; - } - - - public static void PlaceDataWords(ref QRCodeData qrCode, string data, ref List blockedModules) - { - var size = qrCode.ModuleMatrix.Count; - var up = true; - var datawords = new Queue(); - for (int i = 0; i < data.Length; i++) - { - datawords.Enqueue(data[i] != '0'); - } - for (var x = size - 1; x >= 0; x = x - 2) - { - if (x == 6) - x = 5; - for (var yMod = 1; yMod <= size; yMod++) - { - int y; - if (up) - { - y = size - yMod; - if (datawords.Count > 0 && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x] = datawords.Dequeue(); - if (datawords.Count > 0 && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x - 1] = datawords.Dequeue(); - } - else - { - y = yMod - 1; - if (datawords.Count > 0 && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x] = datawords.Dequeue(); - if (datawords.Count > 0 && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x - 1] = datawords.Dequeue(); - } - } - up = !up; - } - } - - public static void ReserveSeperatorAreas(int size, ref List blockedModules) - { - blockedModules.AddRange(new[]{ - new Rectangle(7, 0, 1, 8), - new Rectangle(0, 7, 7, 1), - new Rectangle(0, size-8, 8, 1), - new Rectangle(7, size-7, 1, 7), - new Rectangle(size-8, 0, 1, 8), - new Rectangle(size-7, 7, 7, 1) - }); - } - - public static void ReserveVersionAreas(int size, int version, ref List blockedModules) - { - blockedModules.AddRange(new[]{ - new Rectangle(8, 0, 1, 6), - new Rectangle(8, 7, 1, 1), - new Rectangle(0, 8, 6, 1), - new Rectangle(7, 8, 2, 1), - new Rectangle(size-8, 8, 8, 1), - new Rectangle(8, size-7, 1, 7) - }); - - if (version >= 7) - { - blockedModules.AddRange(new[]{ - new Rectangle(size-11, 0, 3, 6), - new Rectangle(0, size-11, 6, 3) - }); - } - } - public static void PlaceDarkModule(ref QRCodeData qrCode, int version, ref List blockedModules) - { - qrCode.ModuleMatrix[4 * version + 9][8] = true; - blockedModules.Add(new Rectangle(8, 4 * version + 9, 1, 1)); - } - - public static void PlaceFinderPatterns(ref QRCodeData qrCode, ref List blockedModules) - { - var size = qrCode.ModuleMatrix.Count; - int[] locations = { 0, 0, size - 7, 0, 0, size - 7 }; - - for (var i = 0; i < 6; i = i + 2) - { - for (var x = 0; x < 7; x++) - { - for (var y = 0; y < 7; y++) - { - if (!(((x == 1 || x == 5) && y > 0 && y < 6) || (x > 0 && x < 6 && (y == 1 || y == 5)))) - { - qrCode.ModuleMatrix[y + locations[i + 1]][x + locations[i]] = true; - } - } - } - blockedModules.Add(new Rectangle(locations[i], locations[i + 1], 7, 7)); - } - } - - public static void PlaceAlignmentPatterns(ref QRCodeData qrCode, List alignmentPatternLocations, ref List blockedModules) - { - foreach (var loc in alignmentPatternLocations) - { - var alignmentPatternRect = new Rectangle(loc.X, loc.Y, 5, 5); - var blocked = false; - foreach (var blockedRect in blockedModules) - { - if (Intersects(alignmentPatternRect, blockedRect)) - { - blocked = true; - break; - } - } - if (blocked) - continue; - - for (var x = 0; x < 5; x++) - { - for (var y = 0; y < 5; y++) - { - if (y == 0 || y == 4 || x == 0 || x == 4 || (x == 2 && y == 2)) - { - qrCode.ModuleMatrix[loc.Y + y][loc.X + x] = true; - } - } - } - blockedModules.Add(new Rectangle(loc.X, loc.Y, 5, 5)); - } - } - - public static void PlaceTimingPatterns(ref QRCodeData qrCode, ref List blockedModules) - { - var size = qrCode.ModuleMatrix.Count; - for (var i = 8; i < size - 8; i++) - { - if (i % 2 == 0) - { - qrCode.ModuleMatrix[6][i] = true; - qrCode.ModuleMatrix[i][6] = true; - } - } - blockedModules.AddRange(new[]{ - new Rectangle(6, 8, 1, size-16), - new Rectangle(8, 6, size-16, 1) - }); - } - - private static bool Intersects(Rectangle r1, Rectangle r2) - { - return r2.X < r1.X + r1.Width && r1.X < r2.X + r2.Width && r2.Y < r1.Y + r1.Height && r1.Y < r2.Y + r2.Height; - } - - private static bool IsBlocked(Rectangle r1, List blockedModules) - { - foreach (var blockedMod in blockedModules) - { - if (Intersects(blockedMod, r1)) - return true; - } - return false; - } - - private static class MaskPattern - { - public static bool Pattern1(int x, int y) - { - return (x + y) % 2 == 0; - } - - public static bool Pattern2(int x, int y) - { - return y % 2 == 0; - } - - public static bool Pattern3(int x, int y) - { - return x % 3 == 0; - } - - public static bool Pattern4(int x, int y) - { - return (x + y) % 3 == 0; - } - - public static bool Pattern5(int x, int y) - { - return ((int)(Math.Floor(y / 2d) + Math.Floor(x / 3d)) % 2) == 0; - } - - public static bool Pattern6(int x, int y) - { - return ((x * y) % 2) + ((x * y) % 3) == 0; - } - - public static bool Pattern7(int x, int y) - { - return (((x * y) % 2) + ((x * y) % 3)) % 2 == 0; - } - - public static bool Pattern8(int x, int y) - { - return (((x + y) % 2) + ((x * y) % 3)) % 2 == 0; - } - - public static int Score(ref QRCodeData qrCode) - { - int score1 = 0, - score2 = 0, - score3 = 0, - score4 = 0; - var size = qrCode.ModuleMatrix.Count; - - //Penalty 1 - for (var y = 0; y < size; y++) - { - var modInRow = 0; - var modInColumn = 0; - var lastValRow = qrCode.ModuleMatrix[y][0]; - var lastValColumn = qrCode.ModuleMatrix[0][y]; - for (var x = 0; x < size; x++) - { - if (qrCode.ModuleMatrix[y][x] == lastValRow) - modInRow++; - else - modInRow = 1; - if (modInRow == 5) - score1 += 3; - else if (modInRow > 5) - score1++; - lastValRow = qrCode.ModuleMatrix[y][x]; - - - if (qrCode.ModuleMatrix[x][y] == lastValColumn) - modInColumn++; - else - modInColumn = 1; - if (modInColumn == 5) - score1 += 3; - else if (modInColumn > 5) - score1++; - lastValColumn = qrCode.ModuleMatrix[x][y]; - } - } - - - //Penalty 2 - for (var y = 0; y < size - 1; y++) - { - for (var x = 0; x < size - 1; x++) - { - if (qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y][x + 1] && - qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y + 1][x] && - qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y + 1][x + 1]) - score2 += 3; - } - } - - //Penalty 3 - for (var y = 0; y < size; y++) - { - for (var x = 0; x < size - 10; x++) - { - if ((qrCode.ModuleMatrix[y][x] && - !qrCode.ModuleMatrix[y][x + 1] && - qrCode.ModuleMatrix[y][x + 2] && - qrCode.ModuleMatrix[y][x + 3] && - qrCode.ModuleMatrix[y][x + 4] && - !qrCode.ModuleMatrix[y][x + 5] && - qrCode.ModuleMatrix[y][x + 6] && - !qrCode.ModuleMatrix[y][x + 7] && - !qrCode.ModuleMatrix[y][x + 8] && - !qrCode.ModuleMatrix[y][x + 9] && - !qrCode.ModuleMatrix[y][x + 10]) || - (!qrCode.ModuleMatrix[y][x] && - !qrCode.ModuleMatrix[y][x + 1] && - !qrCode.ModuleMatrix[y][x + 2] && - !qrCode.ModuleMatrix[y][x + 3] && - qrCode.ModuleMatrix[y][x + 4] && - !qrCode.ModuleMatrix[y][x + 5] && - qrCode.ModuleMatrix[y][x + 6] && - qrCode.ModuleMatrix[y][x + 7] && - qrCode.ModuleMatrix[y][x + 8] && - !qrCode.ModuleMatrix[y][x + 9] && - qrCode.ModuleMatrix[y][x + 10])) - { - score3 += 40; - } - - if ((qrCode.ModuleMatrix[x][y] && - !qrCode.ModuleMatrix[x + 1][y] && - qrCode.ModuleMatrix[x + 2][y] && - qrCode.ModuleMatrix[x + 3][y] && - qrCode.ModuleMatrix[x + 4][y] && - !qrCode.ModuleMatrix[x + 5][y] && - qrCode.ModuleMatrix[x + 6][y] && - !qrCode.ModuleMatrix[x + 7][y] && - !qrCode.ModuleMatrix[x + 8][y] && - !qrCode.ModuleMatrix[x + 9][y] && - !qrCode.ModuleMatrix[x + 10][y]) || - (!qrCode.ModuleMatrix[x][y] && - !qrCode.ModuleMatrix[x + 1][y] && - !qrCode.ModuleMatrix[x + 2][y] && - !qrCode.ModuleMatrix[x + 3][y] && - qrCode.ModuleMatrix[x + 4][y] && - !qrCode.ModuleMatrix[x + 5][y] && - qrCode.ModuleMatrix[x + 6][y] && - qrCode.ModuleMatrix[x + 7][y] && - qrCode.ModuleMatrix[x + 8][y] && - !qrCode.ModuleMatrix[x + 9][y] && - qrCode.ModuleMatrix[x + 10][y])) - { - score3 += 40; - } - } - } - - //Penalty 4 - double blackModules = 0; - foreach (var row in qrCode.ModuleMatrix) - foreach (bool bit in row) - if (bit) - blackModules++; - - var percent = (blackModules / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count)) * 100; - var prevMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 50) / 5; - var nextMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 45) / 5; - score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; - - return score1 + score2 + score3 + score4; - } - } - - } - - private static List CalculateECCWords(string bitString, ECCInfo eccInfo) - { - var eccWords = eccInfo.ECCPerBlock; - var messagePolynom = CalculateMessagePolynom(bitString); - var generatorPolynom = CalculateGeneratorPolynom(eccWords); - - for (var i = 0; i < messagePolynom.PolyItems.Count; i++) - messagePolynom.PolyItems[i] = new PolynomItem(messagePolynom.PolyItems[i].Coefficient, - messagePolynom.PolyItems[i].Exponent + eccWords); - - for (var i = 0; i < generatorPolynom.PolyItems.Count; i++) - generatorPolynom.PolyItems[i] = new PolynomItem(generatorPolynom.PolyItems[i].Coefficient, - generatorPolynom.PolyItems[i].Exponent + (messagePolynom.PolyItems.Count - 1)); - - var leadTermSource = messagePolynom; - for (var i = 0; (leadTermSource.PolyItems.Count > 0 && leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent > 0); i++) - { - if (leadTermSource.PolyItems[0].Coefficient == 0) - { - leadTermSource.PolyItems.RemoveAt(0); - leadTermSource.PolyItems.Add(new PolynomItem(0, leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent - 1)); - } - else - { - var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, ConvertToAlphaNotation(leadTermSource).PolyItems[0], i); - resPoly = ConvertToDecNotation(resPoly); - resPoly = XORPolynoms(leadTermSource, resPoly); - leadTermSource = resPoly; - } - } - return leadTermSource.PolyItems.Select(x => DecToBin(x.Coefficient, 8)).ToList(); - } - - private static Polynom ConvertToAlphaNotation(Polynom poly) - { - var newPoly = new Polynom(); - for (var i = 0; i < poly.PolyItems.Count; i++) - newPoly.PolyItems.Add( - new PolynomItem( - (poly.PolyItems[i].Coefficient != 0 - ? GetAlphaExpFromIntVal(poly.PolyItems[i].Coefficient) - : 0), poly.PolyItems[i].Exponent)); - return newPoly; - } - - private static Polynom ConvertToDecNotation(Polynom poly) - { - var newPoly = new Polynom(); - for (var i = 0; i < poly.PolyItems.Count; i++) - newPoly.PolyItems.Add(new PolynomItem(GetIntValFromAlphaExp(poly.PolyItems[i].Coefficient), poly.PolyItems[i].Exponent)); - return newPoly; - } - - private static int GetVersion(int length, EncodingMode encMode, ECCLevel eccLevel) - { - - var fittingVersions = capacityTable.Where( - x => x.Details.Any( - y => (y.ErrorCorrectionLevel == eccLevel - && y.CapacityDict[encMode] >= Convert.ToInt32(length) - ) - ) - ).Select(x => new - { - version = x.Version, - capacity = x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel) - .CapacityDict[encMode] - }); - - if (fittingVersions.Any()) - return fittingVersions.Min(x => x.version); - - var maxSizeByte = capacityTable.Where( - x => x.Details.Any( - y => (y.ErrorCorrectionLevel == eccLevel)) - ).Max(x => x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel).CapacityDict[encMode]); - throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encMode.ToString(), maxSizeByte); - } - - private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8) - { - if (forceUtf8) return EncodingMode.Byte; - EncodingMode result = EncodingMode.Numeric; // assume numeric - foreach (char c in plainText) - { - if (IsInRange(c, '0', '9')) continue; // numeric - char.IsDigit() for Latin1 - result = EncodingMode.Alphanumeric; // not numeric, assume alphanumeric - if (IsInRange(c, 'A', 'Z') || alphanumEncTable.Contains(c)) continue; // alphanumeric - return EncodingMode.Byte; // not numeric or alphanumeric, assume byte - } - return result; // either numeric or alphanumeric - } - - private static bool IsInRange(char c, char min, char max) - { - return (uint)(c - min) <= (uint)(max - min); - } - - - private static Polynom CalculateMessagePolynom(string bitString) - { - var messagePol = new Polynom(); - for (var i = bitString.Length / 8 - 1; i >= 0; i--) - { - messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitString.Substring(0, 8)), i)); - bitString = bitString.Remove(0, 8); - } - return messagePol; - } - - - private static Polynom CalculateGeneratorPolynom(int numEccWords) - { - var generatorPolynom = new Polynom(); - generatorPolynom.PolyItems.AddRange(new[]{ - new PolynomItem(0,1), - new PolynomItem(0,0) - }); - for (var i = 1; i <= numEccWords - 1; i++) - { - var multiplierPolynom = new Polynom(); - multiplierPolynom.PolyItems.AddRange(new[]{ - new PolynomItem(0,1), - new PolynomItem(i,0) - }); - - generatorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); - } - - return generatorPolynom; - } - - private static List BinaryStringToBitBlockList(string bitString) - { - const int blockSize = 8; - var numberOfBlocks = (int)Math.Ceiling(bitString.Length / (double)blockSize); - var blocklist = new List(numberOfBlocks); - - for (int i = 0; i < bitString.Length; i += blockSize) - { - blocklist.Add(bitString.Substring(i, blockSize)); - } - - return blocklist; - } - - private static List BinaryStringListToDecList(List binaryStringList) - { - return binaryStringList.Select(binaryString => BinToDec(binaryString)).ToList(); - } - - private static int BinToDec(string binStr) - { - return Convert.ToInt32(binStr, 2); - } - - private static string DecToBin(int decNum) - { - return Convert.ToString(decNum, 2); - } - - private static string DecToBin(int decNum, int padLeftUpTo) - { - var binStr = DecToBin(decNum); - return binStr.PadLeft(padLeftUpTo, '0'); - } - - private static int GetCountIndicatorLength(int version, EncodingMode encMode) - { - if (version < 10) - { - if (encMode == EncodingMode.Numeric) - return 10; - else if (encMode == EncodingMode.Alphanumeric) - return 9; - else - return 8; - } - else if (version < 27) - { - if (encMode == EncodingMode.Numeric) - return 12; - else if (encMode == EncodingMode.Alphanumeric) - return 11; - else if (encMode == EncodingMode.Byte) - return 16; - else - return 10; - } - else - { - if (encMode == EncodingMode.Numeric) - return 14; - else if (encMode == EncodingMode.Alphanumeric) - return 13; - else if (encMode == EncodingMode.Byte) - return 16; - else - return 12; - } - } - - private static int GetDataLength(EncodingMode encoding, string plainText, string codedText, bool forceUtf8) - { - return forceUtf8 || IsUtf8(encoding, plainText) ? (codedText.Length / 8) : plainText.Length; - } - - private static bool IsUtf8(EncodingMode encoding, string plainText) - { - return (encoding == EncodingMode.Byte && !IsValidISO(plainText)); - } - - private static bool IsValidISO(string input) - { - var bytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(input); - //var result = Encoding.GetEncoding("ISO-8859-1").GetString(bytes); - var result = Encoding.GetEncoding("ISO-8859-1").GetString(bytes, 0, bytes.Length); - return String.Equals(input, result); - } - - private static string PlainTextToBinary(string plainText, EncodingMode encMode, EciMode eciMode, bool utf8BOM, bool forceUtf8) - { - switch (encMode) - { - case EncodingMode.Alphanumeric: - return PlainTextToBinaryAlphanumeric(plainText); - case EncodingMode.Numeric: - return PlainTextToBinaryNumeric(plainText); - case EncodingMode.Byte: - return PlainTextToBinaryByte(plainText, eciMode, utf8BOM, forceUtf8); - case EncodingMode.Kanji: - return string.Empty; - case EncodingMode.ECI: - default: - return string.Empty; - } - } - - private static string PlainTextToBinaryNumeric(string plainText) - { - var codeText = string.Empty; - while (plainText.Length >= 3) - { - var dec = Convert.ToInt32(plainText.Substring(0, 3)); - codeText += DecToBin(dec, 10); - plainText = plainText.Substring(3); - - } - if (plainText.Length == 2) - { - var dec = Convert.ToInt32(plainText); - codeText += DecToBin(dec, 7); - } - else if (plainText.Length == 1) - { - var dec = Convert.ToInt32(plainText); - codeText += DecToBin(dec, 4); - } - return codeText; - } - - private static string PlainTextToBinaryAlphanumeric(string plainText) - { - var codeText = string.Empty; - while (plainText.Length >= 2) - { - var token = plainText.Substring(0, 2); - var dec = alphanumEncDict[token[0]] * 45 + alphanumEncDict[token[1]]; - codeText += DecToBin(dec, 11); - plainText = plainText.Substring(2); - - } - if (plainText.Length > 0) - { - codeText += DecToBin(alphanumEncDict[plainText[0]], 6); - } - return codeText; - } - - private string PlainTextToBinaryECI(string plainText) - { - var codeText = string.Empty; - byte[] _bytes = Encoding.GetEncoding("ascii").GetBytes(plainText); - foreach (byte _byte in _bytes) - { - codeText += DecToBin(_byte, 8); - } - return codeText; - } - - private static string ConvertToIso8859(string value, string Iso = "ISO-8859-2") - { - Encoding iso = Encoding.GetEncoding(Iso); - Encoding utf8 = Encoding.UTF8; - byte[] utfBytes = utf8.GetBytes(value); - byte[] isoBytes = Encoding.Convert(utf8, iso, utfBytes); -#if NETFRAMEWORK || NETSTANDARD2_0 - return iso.GetString(isoBytes); -#else - return iso.GetString(isoBytes, 0, isoBytes.Length); -#endif - } - - private static string PlainTextToBinaryByte(string plainText, EciMode eciMode, bool utf8BOM, bool forceUtf8) - { - byte[] codeBytes; - var codeText = string.Empty; - - if (IsValidISO(plainText) && !forceUtf8) - codeBytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(plainText); - else - { - switch (eciMode) - { - case EciMode.Iso8859_1: - codeBytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(ConvertToIso8859(plainText, "ISO-8859-1")); - break; - case EciMode.Iso8859_2: - codeBytes = Encoding.GetEncoding("ISO-8859-2").GetBytes(ConvertToIso8859(plainText, "ISO-8859-2")); - break; - case EciMode.Default: - case EciMode.Utf8: - default: - codeBytes = utf8BOM ? Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes(plainText)).ToArray() : Encoding.UTF8.GetBytes(plainText); - break; - } - } - - foreach (var b in codeBytes) - codeText += DecToBin(b, 8); - - return codeText; - } - - private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) - { - var resultPolynom = new Polynom(); - Polynom longPoly, shortPoly; - if (messagePolynom.PolyItems.Count >= resPolynom.PolyItems.Count) - { - longPoly = messagePolynom; - shortPoly = resPolynom; - } - else - { - longPoly = resPolynom; - shortPoly = messagePolynom; - } - - for (var i = 0; i < longPoly.PolyItems.Count; i++) - { - var polItemRes = new PolynomItem - ( - - longPoly.PolyItems[i].Coefficient ^ - (shortPoly.PolyItems.Count > i ? shortPoly.PolyItems[i].Coefficient : 0), - messagePolynom.PolyItems[0].Exponent - i - ); - resultPolynom.PolyItems.Add(polItemRes); - } - resultPolynom.PolyItems.RemoveAt(0); - return resultPolynom; - } - - - private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, PolynomItem leadTerm, int lowerExponentBy) - { - var resultPolynom = new Polynom(); - foreach (var polItemBase in genPolynom.PolyItems) - { - var polItemRes = new PolynomItem( - - (polItemBase.Coefficient + leadTerm.Coefficient) % 255, - polItemBase.Exponent - lowerExponentBy - ); - resultPolynom.PolyItems.Add(polItemRes); - } - return resultPolynom; - } - - - private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polynomMultiplier) - { - var resultPolynom = new Polynom(); - foreach (var polItemBase in polynomMultiplier.PolyItems) - { - foreach (var polItemMulti in polynomBase.PolyItems) - { - var polItemRes = new PolynomItem - ( - ShrinkAlphaExp(polItemBase.Coefficient + polItemMulti.Coefficient), - (polItemBase.Exponent + polItemMulti.Exponent) - ); - resultPolynom.PolyItems.Add(polItemRes); - } - } - var exponentsToGlue = resultPolynom.PolyItems.GroupBy(x => x.Exponent).Where(x => x.Count() > 1).Select(x => x.First().Exponent); - var toGlue = exponentsToGlue as IList ?? exponentsToGlue.ToList(); - var gluedPolynoms = new List(toGlue.Count); - foreach (var exponent in toGlue) - { - var coefficient = resultPolynom.PolyItems.Where(x => x.Exponent == exponent).Aggregate(0, (current, polynomOld) - => current ^ GetIntValFromAlphaExp(polynomOld.Coefficient)); - var polynomFixed = new PolynomItem(GetAlphaExpFromIntVal(coefficient), exponent); - gluedPolynoms.Add(polynomFixed); - } - resultPolynom.PolyItems.RemoveAll(x => toGlue.Contains(x.Exponent)); - resultPolynom.PolyItems.AddRange(gluedPolynoms); - resultPolynom.PolyItems.Sort((x, y) => -x.Exponent.CompareTo(y.Exponent)); - return resultPolynom; - } - - private static int GetIntValFromAlphaExp(int exp) - { - return galoisField.Find(alog => alog.ExponentAlpha == exp).IntegerValue; - } - - private static int GetAlphaExpFromIntVal(int intVal) - { - return galoisField.Find(alog => alog.IntegerValue == intVal).ExponentAlpha; - } - - private static int ShrinkAlphaExp(int alphaExp) - { - // ReSharper disable once PossibleLossOfFraction - return (int)((alphaExp % 256) + Math.Floor((double)(alphaExp / 256))); - } - - private static Dictionary CreateAlphanumEncDict() - { - var localAlphanumEncDict = new Dictionary(45); - //Add numbers - for (int i = 0; i < 10; i++) - localAlphanumEncDict.Add(i.ToString()[0], i); - //Add chars - for (char c = 'A'; c <= 'Z'; c++) - localAlphanumEncDict.Add(c, localAlphanumEncDict.Count()); - //Add special chars - for (int i = 0; i < alphanumEncTable.Length; i++) - localAlphanumEncDict.Add(alphanumEncTable[i], localAlphanumEncDict.Count()); - return localAlphanumEncDict; - } - - private static List CreateAlignmentPatternTable() - { - var localAlignmentPatternTable = new List(40); - - for (var i = 0; i < (7 * 40); i = i + 7) - { - var points = new List(); - for (var x = 0; x < 7; x++) - { - if (alignmentPatternBaseValues[i + x] != 0) - { - for (var y = 0; y < 7; y++) - { - if (alignmentPatternBaseValues[i + y] != 0) - { - var p = new Point(alignmentPatternBaseValues[i + x] - 2, alignmentPatternBaseValues[i + y] - 2); - if (!points.Contains(p)) - points.Add(p); - } - } - } - } - - localAlignmentPatternTable.Add(new AlignmentPattern() - { - Version = (i + 7) / 7, - PatternPositions = points - } - ); - } - return localAlignmentPatternTable; - } - - - private static List CreateCapacityECCTable() - { - var localCapacityECCTable = new List(160); - for (var i = 0; i < (4 * 6 * 40); i = i + (4 * 6)) - { - localCapacityECCTable.AddRange( - new[] - { - new ECCInfo( - (i+24) / 24, - ECCLevel.L, - capacityECCBaseValues[i], - capacityECCBaseValues[i+1], - capacityECCBaseValues[i+2], - capacityECCBaseValues[i+3], - capacityECCBaseValues[i+4], - capacityECCBaseValues[i+5]), - new ECCInfo - ( - version: (i + 24) / 24, - errorCorrectionLevel: ECCLevel.M, - totalDataCodewords: capacityECCBaseValues[i+6], - eccPerBlock: capacityECCBaseValues[i+7], - blocksInGroup1: capacityECCBaseValues[i+8], - codewordsInGroup1: capacityECCBaseValues[i+9], - blocksInGroup2: capacityECCBaseValues[i+10], - codewordsInGroup2: capacityECCBaseValues[i+11] - ), - new ECCInfo - ( - version: (i + 24) / 24, - errorCorrectionLevel: ECCLevel.Q, - totalDataCodewords: capacityECCBaseValues[i+12], - eccPerBlock: capacityECCBaseValues[i+13], - blocksInGroup1: capacityECCBaseValues[i+14], - codewordsInGroup1: capacityECCBaseValues[i+15], - blocksInGroup2: capacityECCBaseValues[i+16], - codewordsInGroup2: capacityECCBaseValues[i+17] - ), - new ECCInfo - ( - version: (i + 24) / 24, - errorCorrectionLevel: ECCLevel.H, - totalDataCodewords: capacityECCBaseValues[i+18], - eccPerBlock: capacityECCBaseValues[i+19], - blocksInGroup1: capacityECCBaseValues[i+20], - codewordsInGroup1: capacityECCBaseValues[i+21], - blocksInGroup2: capacityECCBaseValues[i+22], - codewordsInGroup2: capacityECCBaseValues[i+23] - ) - }); - } - return localCapacityECCTable; - } - - private static List CreateCapacityTable() - { - var localCapacityTable = new List(40); - for (var i = 0; i < (16 * 40); i = i + 16) - { - localCapacityTable.Add(new VersionInfo( - - (i + 16) / 16, - new List(4) - { - new VersionInfoDetails( - ECCLevel.L, - new Dictionary(){ - { EncodingMode.Numeric, capacityBaseValues[i] }, - { EncodingMode.Alphanumeric, capacityBaseValues[i+1] }, - { EncodingMode.Byte, capacityBaseValues[i+2] }, - { EncodingMode.Kanji, capacityBaseValues[i+3] }, - } - ), - new VersionInfoDetails( - ECCLevel.M, - new Dictionary(){ - { EncodingMode.Numeric, capacityBaseValues[i+4] }, - { EncodingMode.Alphanumeric, capacityBaseValues[i+5] }, - { EncodingMode.Byte, capacityBaseValues[i+6] }, - { EncodingMode.Kanji, capacityBaseValues[i+7] }, - } - ), - new VersionInfoDetails( - ECCLevel.Q, - new Dictionary(){ - { EncodingMode.Numeric, capacityBaseValues[i+8] }, - { EncodingMode.Alphanumeric, capacityBaseValues[i+9] }, - { EncodingMode.Byte, capacityBaseValues[i+10] }, - { EncodingMode.Kanji, capacityBaseValues[i+11] }, - } - ), - new VersionInfoDetails( - ECCLevel.H, - new Dictionary(){ - { EncodingMode.Numeric, capacityBaseValues[i+12] }, - { EncodingMode.Alphanumeric, capacityBaseValues[i+13] }, - { EncodingMode.Byte, capacityBaseValues[i+14] }, - { EncodingMode.Kanji, capacityBaseValues[i+15] }, - } - ) - } - )); - } - return localCapacityTable; - } - - private static List CreateAntilogTable() - { - var localGaloisField = new List(256); - - int gfItem = 1; - for (var i = 0; i < 256; i++) - { - localGaloisField.Add(new Antilog(i, gfItem)); - gfItem *= 2; - if (gfItem > 255) - gfItem ^= 285; - } - return localGaloisField; - } - - /// - /// Error correction level. These define the tolerance levels for how much of the code can be lost before the code cannot be recovered. - /// - public enum ECCLevel - { - /// - /// 7% may be lost before recovery is not possible - /// - L, - /// - /// 15% may be lost before recovery is not possible - /// - M, - /// - /// 25% may be lost before recovery is not possible - /// - Q, - /// - /// 30% may be lost before recovery is not possible - /// - H - } - - private enum EncodingMode - { - Numeric = 1, - Alphanumeric = 2, - Byte = 4, - Kanji = 8, - ECI = 7 - } - - private struct AlignmentPattern - { - public int Version; - public List PatternPositions; - } - - private struct CodewordBlock - { - public CodewordBlock(int groupNumber, int blockNumber, string bitString, List codeWords, - List eccWords, List codeWordsInt, List eccWordsInt) - { - this.GroupNumber = groupNumber; - this.BlockNumber = blockNumber; - this.BitString = bitString; - this.CodeWords = codeWords; - this.ECCWords = eccWords; - this.CodeWordsInt = codeWordsInt; - this.ECCWordsInt = eccWordsInt; - } - - public int GroupNumber; // { get; } - public int BlockNumber; // { get; } - public string BitString; // { get; } - public List CodeWords; // { get; } - public List CodeWordsInt; // { get; } - public List ECCWords; // { get; } - public List ECCWordsInt; // { get; } - } - - private struct ECCInfo - { - public ECCInfo(int version, ECCLevel errorCorrectionLevel, int totalDataCodewords, int eccPerBlock, int blocksInGroup1, - int codewordsInGroup1, int blocksInGroup2, int codewordsInGroup2) - { - this.Version = version; - this.ErrorCorrectionLevel = errorCorrectionLevel; - this.TotalDataCodewords = totalDataCodewords; - this.ECCPerBlock = eccPerBlock; - this.BlocksInGroup1 = blocksInGroup1; - this.CodewordsInGroup1 = codewordsInGroup1; - this.BlocksInGroup2 = blocksInGroup2; - this.CodewordsInGroup2 = codewordsInGroup2; - } - public int Version; // { get; } - public ECCLevel ErrorCorrectionLevel; // { get; } - public int TotalDataCodewords; // { get; } - public int ECCPerBlock; // { get; } - public int BlocksInGroup1; // { get; } - public int CodewordsInGroup1; // { get; } - public int BlocksInGroup2; // { get; } - public int CodewordsInGroup2; // { get; } - } - - private struct VersionInfo - { - public VersionInfo(int version, List versionInfoDetails) - { - this.Version = version; - this.Details = versionInfoDetails; - } - public int Version; // { get; } - public List Details; // { get; } - } - - private struct VersionInfoDetails - { - public VersionInfoDetails(ECCLevel errorCorrectionLevel, Dictionary capacityDict) - { - this.ErrorCorrectionLevel = errorCorrectionLevel; - this.CapacityDict = capacityDict; - } - - public ECCLevel ErrorCorrectionLevel; // { get; } - public Dictionary CapacityDict; // { get; } - } - - private struct Antilog - { - public Antilog(int exponentAlpha, int integerValue) - { - this.ExponentAlpha = exponentAlpha; - this.IntegerValue = integerValue; - } - public int ExponentAlpha; // { get; } - public int IntegerValue; // { get; } - } - - private struct PolynomItem - { - public PolynomItem(int coefficient, int exponent) - { - this.Coefficient = coefficient; - this.Exponent = exponent; - } - - public int Coefficient; // { get; } - public int Exponent; // { get; } - } - - private class Polynom - { - public Polynom() - { - this.PolyItems = new List(); - } - - public List PolyItems; // { get; set; } - - public override string ToString() - { - var sb = new StringBuilder(); - //this.PolyItems.ForEach(x => sb.Append("a^" + x.Coefficient + "*x^" + x.Exponent + " + ")); - foreach (var polyItem in this.PolyItems) - { - sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + "); - } - - return sb.ToString().TrimEnd(new[] { ' ', '+' }); - } - } - - private class Point - { - public int X; // { get; } - public int Y; // { get; } - public Point(int x, int y) - { - this.X = x; - this.Y = y; - } - } - - private class Rectangle - { - public int X; // { get; } - public int Y; // { get; } - public int Width; // { get; } - public int Height; // { get; } - - public Rectangle(int x, int y, int w, int h) - { - this.X = x; - this.Y = y; - this.Width = w; - this.Height = h; - } - } - - public void Dispose() - { - // left for back-compat - } - } -} - \ No newline at end of file diff --git a/version.info b/version.info index 9b6ac48..36bcdfc 100644 --- a/version.info +++ b/version.info @@ -1,5 +1,5 @@ : -KeePassOTP:0.18.2 +KeePassOTP:0.19 KeePassOTP!de:11 KeePassOTP!fr:3 :