From 77623dd49e1ad1b8dbf7c27a03cee4fb361f411c Mon Sep 17 00:00:00 2001 From: Shane32 Date: Fri, 3 May 2024 20:03:13 -0400 Subject: [PATCH 1/3] Refactor QRCodeGenerator into separate files --- QRCoder/QRCodeGenerator.AlignmentPattern.cs | 13 + QRCoder/QRCodeGenerator.CodewordBlock.cs | 17 + QRCoder/QRCodeGenerator.ECCInfo.cs | 29 + QRCoder/QRCodeGenerator.ECCLevel.cs | 28 + QRCoder/QRCodeGenerator.EncodingMode.cs | 14 + QRCoder/QRCodeGenerator.ModulePlacer.cs | 475 +++++++++++++ QRCoder/QRCodeGenerator.Point.cs | 24 + QRCoder/QRCodeGenerator.Polynom.cs | 33 + QRCoder/QRCodeGenerator.PolynomItem.cs | 17 + QRCoder/QRCodeGenerator.Rectangle.cs | 21 + QRCoder/QRCodeGenerator.VersionInfo.cs | 18 + QRCoder/QRCodeGenerator.VersionInfoDetails.cs | 19 + QRCoder/QRCodeGenerator.cs | 642 +----------------- 13 files changed, 712 insertions(+), 638 deletions(-) create mode 100644 QRCoder/QRCodeGenerator.AlignmentPattern.cs create mode 100644 QRCoder/QRCodeGenerator.CodewordBlock.cs create mode 100644 QRCoder/QRCodeGenerator.ECCInfo.cs create mode 100644 QRCoder/QRCodeGenerator.ECCLevel.cs create mode 100644 QRCoder/QRCodeGenerator.EncodingMode.cs create mode 100644 QRCoder/QRCodeGenerator.ModulePlacer.cs create mode 100644 QRCoder/QRCodeGenerator.Point.cs create mode 100644 QRCoder/QRCodeGenerator.Polynom.cs create mode 100644 QRCoder/QRCodeGenerator.PolynomItem.cs create mode 100644 QRCoder/QRCodeGenerator.Rectangle.cs create mode 100644 QRCoder/QRCodeGenerator.VersionInfo.cs create mode 100644 QRCoder/QRCodeGenerator.VersionInfoDetails.cs diff --git a/QRCoder/QRCodeGenerator.AlignmentPattern.cs b/QRCoder/QRCodeGenerator.AlignmentPattern.cs new file mode 100644 index 00000000..762107fb --- /dev/null +++ b/QRCoder/QRCodeGenerator.AlignmentPattern.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private struct AlignmentPattern + { + public int Version; + public List PatternPositions; + } + } +} diff --git a/QRCoder/QRCodeGenerator.CodewordBlock.cs b/QRCoder/QRCodeGenerator.CodewordBlock.cs new file mode 100644 index 00000000..a4db5218 --- /dev/null +++ b/QRCoder/QRCodeGenerator.CodewordBlock.cs @@ -0,0 +1,17 @@ +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private struct CodewordBlock + { + public CodewordBlock(byte[] codeWords, byte[] eccWords) + { + this.CodeWords = codeWords; + this.ECCWords = eccWords; + } + + public byte[] CodeWords { get; } + public byte[] ECCWords { get; } + } + } +} diff --git a/QRCoder/QRCodeGenerator.ECCInfo.cs b/QRCoder/QRCodeGenerator.ECCInfo.cs new file mode 100644 index 00000000..d4c6c5ef --- /dev/null +++ b/QRCoder/QRCodeGenerator.ECCInfo.cs @@ -0,0 +1,29 @@ +namespace QRCoder +{ + public partial class QRCodeGenerator + { + 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; } + } + } +} diff --git a/QRCoder/QRCodeGenerator.ECCLevel.cs b/QRCoder/QRCodeGenerator.ECCLevel.cs new file mode 100644 index 00000000..dc97bf3c --- /dev/null +++ b/QRCoder/QRCodeGenerator.ECCLevel.cs @@ -0,0 +1,28 @@ +namespace QRCoder +{ + public partial class QRCodeGenerator + { + /// + /// 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 + } + } +} diff --git a/QRCoder/QRCodeGenerator.EncodingMode.cs b/QRCoder/QRCodeGenerator.EncodingMode.cs new file mode 100644 index 00000000..671b6dc2 --- /dev/null +++ b/QRCoder/QRCodeGenerator.EncodingMode.cs @@ -0,0 +1,14 @@ +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private enum EncodingMode + { + Numeric = 1, + Alphanumeric = 2, + Byte = 4, + Kanji = 8, + ECI = 7 + } + } +} diff --git a/QRCoder/QRCodeGenerator.ModulePlacer.cs b/QRCoder/QRCodeGenerator.ModulePlacer.cs new file mode 100644 index 00000000..bbf5d2b9 --- /dev/null +++ b/QRCoder/QRCodeGenerator.ModulePlacer.cs @@ -0,0 +1,475 @@ +using System; +using System.Collections.Generic; +using System.Collections; + +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private static class ModulePlacer + { + public static void AddQuietZone(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++) + { + qrCode.ModuleMatrix[i].Length += 8; + ShiftAwayFromBit0(qrCode.ModuleMatrix[i], 4); + } + } + + public static void PlaceVersion(QRCodeData qrCode, BitArray versionStr) + { + var size = qrCode.ModuleMatrix.Count; + + for (var x = 0; x < 6; x++) + { + for (var y = 0; y < 3; y++) + { + qrCode.ModuleMatrix[y + size - 11][x] = versionStr[17 - (x * 3 + y)]; + qrCode.ModuleMatrix[x][y + size - 11] = versionStr[17 - (x * 3 + y)]; + } + } + } + + public static void PlaceFormat(QRCodeData qrCode, BitArray formatStr) + { + var size = qrCode.ModuleMatrix.Count; + + // { x1, y1, x2, y2 } i + // =============================== + // { 8, 0, size - 1, 8 }, // 0 + // { 8, 1, size - 2, 8 }, // 1 + // { 8, 2, size - 3, 8 }, // 2 + // { 8, 3, size - 4, 8 }, // 3 + // { 8, 4, size - 5, 8 }, // 4 + // { 8, 5, size - 6, 8 }, // 5 + // { 8, 7, size - 7, 8 }, // 6 + // { 8, 8, size - 8, 8 }, // 7 + // { 7, 8, 8, size - 7 }, // 8 + // { 5, 8, 8, size - 6 }, // 9 + // { 4, 8, 8, size - 5 }, // 10 + // { 3, 8, 8, size - 4 }, // 11 + // { 2, 8, 8, size - 3 }, // 12 + // { 1, 8, 8, size - 2 }, // 13 + // { 0, 8, 8, size - 1 } }; // 14 + + for (var i = 0; i < 15; i++) + { + // values computed to follow table above + var x1 = i < 8 ? 8 : i == 8 ? 7 : 14 - i; + var y1 = i < 6 ? i : i < 7 ? i + 1 : 8; + var x2 = i < 8 ? size - 1 - i : 8; + var y2 = i < 8 ? 8 : size - (15 - i); + + qrCode.ModuleMatrix[y1][x1] = formatStr[14 - i]; + qrCode.ModuleMatrix[y2][x2] = formatStr[14 - i]; + } + } + + public static int MaskCode(QRCodeData qrCode, int version, List blockedModules, ECCLevel eccLevel) + { + int? selectedPattern = null; + var patternScore = 0; + + var size = qrCode.ModuleMatrix.Count; + + var qrTemp = new QRCodeData(version); + foreach (var pattern in MaskPattern.Patterns) + { + // reset qrTemp to qrCode + 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(qrTemp, formatStr); + if (version >= 7) + { + var versionString = GetVersionString(version); + ModulePlacer.PlaceVersion(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(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] ^= MaskPattern.Patterns[selectedPattern.Value](x, y); + qrCode.ModuleMatrix[x][y] ^= MaskPattern.Patterns[selectedPattern.Value](y, x); + } + } + + if (!IsBlocked(new Rectangle(x, x, 1, 1), blockedModules)) + { + qrCode.ModuleMatrix[x][x] ^= MaskPattern.Patterns[selectedPattern.Value](x, x); + } + } + return selectedPattern.Value - 1; + } + + + public static void PlaceDataWords(QRCodeData qrCode, BitArray data, List blockedModules) + { + var size = qrCode.ModuleMatrix.Count; + var up = true; + var index = 0; + var count = data.Length; + 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 (index < count && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x] = data[index++]; + if (index < count && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x - 1] = data[index++]; + } + else + { + y = yMod - 1; + if (index < count && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x] = data[index++]; + if (index < count && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x - 1] = data[index++]; + } + } + up = !up; + } + } + + public static void ReserveSeperatorAreas(int size, List blockedModules) + { + blockedModules.Add(new Rectangle(7, 0, 1, 8)); + blockedModules.Add(new Rectangle(0, 7, 7, 1)); + blockedModules.Add(new Rectangle(0, size - 8, 8, 1)); + blockedModules.Add(new Rectangle(7, size - 7, 1, 7)); + blockedModules.Add(new Rectangle(size - 8, 0, 1, 8)); + blockedModules.Add(new Rectangle(size - 7, 7, 7, 1)); + } + + public static void ReserveVersionAreas(int size, int version, List blockedModules) + { + blockedModules.Add(new Rectangle(8, 0, 1, 6)); + blockedModules.Add(new Rectangle(8, 7, 1, 1)); + blockedModules.Add(new Rectangle(0, 8, 6, 1)); + blockedModules.Add(new Rectangle(7, 8, 2, 1)); + blockedModules.Add(new Rectangle(size - 8, 8, 8, 1)); + blockedModules.Add(new Rectangle(8, size - 7, 1, 7)); + + if (version >= 7) + { + blockedModules.Add(new Rectangle(size - 11, 0, 3, 6)); + blockedModules.Add(new Rectangle(0, size - 11, 6, 3)); + } + } + public static void PlaceDarkModule(QRCodeData qrCode, int version, List blockedModules) + { + qrCode.ModuleMatrix[4 * version + 9][8] = true; + blockedModules.Add(new Rectangle(8, 4 * version + 9, 1, 1)); + } + + public static void PlaceFinderPatterns(QRCodeData qrCode, List blockedModules) + { + var size = qrCode.ModuleMatrix.Count; + + for (var i = 0; i < 3; i++) + { + var locationX = i == 1 ? size - 7 : 0; + var locationY = i == 2 ? size - 7 : 0; + 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 + locationY][x + locationX] = true; + } + } + } + blockedModules.Add(new Rectangle(locationX, locationY, 7, 7)); + } + } + + public static void PlaceAlignmentPatterns(QRCodeData qrCode, List alignmentPatternLocations, 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(QRCodeData qrCode, 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.Add(new Rectangle(6, 8, 1, size - 16)); + blockedModules.Add(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 readonly Dictionary> Patterns = + 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 } + }; + + 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(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 + int blackModules = 0; + foreach (var bitArray in qrCode.ModuleMatrix) + for (var x = 0; x < size; x++) + if (bitArray[x]) + blackModules++; + + var percentDiv5 = blackModules * 20 / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count); + var prevMultipleOf5 = Math.Abs(percentDiv5 - 10); + var nextMultipleOf5 = Math.Abs(percentDiv5 - 9); + score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; + + return (score1 + score2) + (score3 + score4); + } + } + + } + } +} diff --git a/QRCoder/QRCodeGenerator.Point.cs b/QRCoder/QRCodeGenerator.Point.cs new file mode 100644 index 00000000..766655f6 --- /dev/null +++ b/QRCoder/QRCodeGenerator.Point.cs @@ -0,0 +1,24 @@ +using System; + +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private readonly struct Point : IEquatable + { + public int X { get; } + public int Y { get; } + public Point(int x, int y) + { + this.X = x; + this.Y = y; + } + + public bool Equals(Point other) + { + return this.X == other.X && this.Y == other.Y; + } + + } + } +} diff --git a/QRCoder/QRCodeGenerator.Polynom.cs b/QRCoder/QRCodeGenerator.Polynom.cs new file mode 100644 index 00000000..10d62017 --- /dev/null +++ b/QRCoder/QRCodeGenerator.Polynom.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Text; + +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private struct Polynom + { + public Polynom(int count) + { + this.PolyItems = new List(count); + } + + public List PolyItems { get; set; } + + public override string ToString() + { + var sb = new StringBuilder(); + + foreach (var polyItem in this.PolyItems) + { + sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + "); + } + + if (sb.Length > 0) + sb.Length -= 3; + + return sb.ToString(); + } + } + } +} diff --git a/QRCoder/QRCodeGenerator.PolynomItem.cs b/QRCoder/QRCodeGenerator.PolynomItem.cs new file mode 100644 index 00000000..92bea0a1 --- /dev/null +++ b/QRCoder/QRCodeGenerator.PolynomItem.cs @@ -0,0 +1,17 @@ +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private struct PolynomItem + { + public PolynomItem(int coefficient, int exponent) + { + this.Coefficient = coefficient; + this.Exponent = exponent; + } + + public int Coefficient { get; } + public int Exponent { get; } + } + } +} diff --git a/QRCoder/QRCodeGenerator.Rectangle.cs b/QRCoder/QRCodeGenerator.Rectangle.cs new file mode 100644 index 00000000..39db9ec3 --- /dev/null +++ b/QRCoder/QRCodeGenerator.Rectangle.cs @@ -0,0 +1,21 @@ +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private readonly struct 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; + } + } + } +} diff --git a/QRCoder/QRCodeGenerator.VersionInfo.cs b/QRCoder/QRCodeGenerator.VersionInfo.cs new file mode 100644 index 00000000..5d4e301a --- /dev/null +++ b/QRCoder/QRCodeGenerator.VersionInfo.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private struct VersionInfo + { + public VersionInfo(int version, List versionInfoDetails) + { + this.Version = version; + this.Details = versionInfoDetails; + } + public int Version { get; } + public List Details { get; } + } + } +} diff --git a/QRCoder/QRCodeGenerator.VersionInfoDetails.cs b/QRCoder/QRCodeGenerator.VersionInfoDetails.cs new file mode 100644 index 00000000..b452a011 --- /dev/null +++ b/QRCoder/QRCodeGenerator.VersionInfoDetails.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private struct VersionInfoDetails + { + public VersionInfoDetails(ECCLevel errorCorrectionLevel, Dictionary capacityDict) + { + this.ErrorCorrectionLevel = errorCorrectionLevel; + this.CapacityDict = capacityDict; + } + + public ECCLevel ErrorCorrectionLevel { get; } + public Dictionary CapacityDict { get; } + } + } +} diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 19ee6fba..e6841673 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1,15 +1,14 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Collections; +using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Runtime.CompilerServices; -using System.Reflection; +using System.Text; namespace QRCoder { - public class QRCodeGenerator : IDisposable + public partial 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 }; @@ -403,472 +402,6 @@ private static BitArray GetVersionString(int version) return vStr; } - private static class ModulePlacer - { - public static void AddQuietZone(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++) - { - qrCode.ModuleMatrix[i].Length += 8; - ShiftAwayFromBit0(qrCode.ModuleMatrix[i], 4); - } - } - - public static void PlaceVersion(QRCodeData qrCode, BitArray versionStr) - { - var size = qrCode.ModuleMatrix.Count; - - for (var x = 0; x < 6; x++) - { - for (var y = 0; y < 3; y++) - { - qrCode.ModuleMatrix[y + size - 11][x] = versionStr[17 - (x * 3 + y)]; - qrCode.ModuleMatrix[x][y + size - 11] = versionStr[17 - (x * 3 + y)]; - } - } - } - - public static void PlaceFormat(QRCodeData qrCode, BitArray formatStr) - { - var size = qrCode.ModuleMatrix.Count; - - // { x1, y1, x2, y2 } i - // =============================== - // { 8, 0, size - 1, 8 }, // 0 - // { 8, 1, size - 2, 8 }, // 1 - // { 8, 2, size - 3, 8 }, // 2 - // { 8, 3, size - 4, 8 }, // 3 - // { 8, 4, size - 5, 8 }, // 4 - // { 8, 5, size - 6, 8 }, // 5 - // { 8, 7, size - 7, 8 }, // 6 - // { 8, 8, size - 8, 8 }, // 7 - // { 7, 8, 8, size - 7 }, // 8 - // { 5, 8, 8, size - 6 }, // 9 - // { 4, 8, 8, size - 5 }, // 10 - // { 3, 8, 8, size - 4 }, // 11 - // { 2, 8, 8, size - 3 }, // 12 - // { 1, 8, 8, size - 2 }, // 13 - // { 0, 8, 8, size - 1 } }; // 14 - - for (var i = 0; i < 15; i++) - { - // values computed to follow table above - var x1 = i < 8 ? 8 : i == 8 ? 7 : 14 - i; - var y1 = i < 6 ? i : i < 7 ? i + 1 : 8; - var x2 = i < 8 ? size - 1 - i : 8; - var y2 = i < 8 ? 8 : size - (15 - i); - - qrCode.ModuleMatrix[y1][x1] = formatStr[14 - i]; - qrCode.ModuleMatrix[y2][x2] = formatStr[14 - i]; - } - } - - public static int MaskCode(QRCodeData qrCode, int version, List blockedModules, ECCLevel eccLevel) - { - int? selectedPattern = null; - var patternScore = 0; - - var size = qrCode.ModuleMatrix.Count; - - var qrTemp = new QRCodeData(version); - foreach (var pattern in MaskPattern.Patterns) - { - // reset qrTemp to qrCode - 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(qrTemp, formatStr); - if (version >= 7) - { - var versionString = GetVersionString(version); - ModulePlacer.PlaceVersion(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(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] ^= MaskPattern.Patterns[selectedPattern.Value](x, y); - qrCode.ModuleMatrix[x][y] ^= MaskPattern.Patterns[selectedPattern.Value](y, x); - } - } - - if (!IsBlocked(new Rectangle(x, x, 1, 1), blockedModules)) - { - qrCode.ModuleMatrix[x][x] ^= MaskPattern.Patterns[selectedPattern.Value](x, x); - } - } - return selectedPattern.Value - 1; - } - - - public static void PlaceDataWords(QRCodeData qrCode, BitArray data, List blockedModules) - { - var size = qrCode.ModuleMatrix.Count; - var up = true; - var index = 0; - var count = data.Length; - 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 (index < count && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x] = data[index++]; - if (index < count && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x - 1] = data[index++]; - } - else - { - y = yMod - 1; - if (index < count && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x] = data[index++]; - if (index < count && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x - 1] = data[index++]; - } - } - up = !up; - } - } - - public static void ReserveSeperatorAreas(int size, List blockedModules) - { - blockedModules.Add(new Rectangle(7, 0, 1, 8)); - blockedModules.Add(new Rectangle(0, 7, 7, 1)); - blockedModules.Add(new Rectangle(0, size - 8, 8, 1)); - blockedModules.Add(new Rectangle(7, size - 7, 1, 7)); - blockedModules.Add(new Rectangle(size - 8, 0, 1, 8)); - blockedModules.Add(new Rectangle(size - 7, 7, 7, 1)); - } - - public static void ReserveVersionAreas(int size, int version, List blockedModules) - { - blockedModules.Add(new Rectangle(8, 0, 1, 6)); - blockedModules.Add(new Rectangle(8, 7, 1, 1)); - blockedModules.Add(new Rectangle(0, 8, 6, 1)); - blockedModules.Add(new Rectangle(7, 8, 2, 1)); - blockedModules.Add(new Rectangle(size - 8, 8, 8, 1)); - blockedModules.Add(new Rectangle(8, size - 7, 1, 7)); - - if (version >= 7) - { - blockedModules.Add(new Rectangle(size - 11, 0, 3, 6)); - blockedModules.Add(new Rectangle(0, size - 11, 6, 3)); - } - } - public static void PlaceDarkModule(QRCodeData qrCode, int version, List blockedModules) - { - qrCode.ModuleMatrix[4 * version + 9][8] = true; - blockedModules.Add(new Rectangle(8, 4 * version + 9, 1, 1)); - } - - public static void PlaceFinderPatterns(QRCodeData qrCode, List blockedModules) - { - var size = qrCode.ModuleMatrix.Count; - - for (var i = 0; i < 3; i++) - { - var locationX = i == 1 ? size - 7 : 0; - var locationY = i == 2 ? size - 7 : 0; - 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 + locationY][x + locationX] = true; - } - } - } - blockedModules.Add(new Rectangle(locationX, locationY, 7, 7)); - } - } - - public static void PlaceAlignmentPatterns(QRCodeData qrCode, List alignmentPatternLocations, 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(QRCodeData qrCode, 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.Add(new Rectangle(6, 8, 1, size - 16)); - blockedModules.Add(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 readonly Dictionary> Patterns = - 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 } - }; - - 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(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 - int blackModules = 0; - foreach (var bitArray in qrCode.ModuleMatrix) - for (var x = 0; x < size; x++) - if (bitArray[x]) - blackModules++; - - var percentDiv5 = blackModules * 20 / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count); - var prevMultipleOf5 = Math.Abs(percentDiv5 - 10); - var nextMultipleOf5 = Math.Abs(percentDiv5 - 9); - score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; - - return (score1 + score2) + (score3 + score4); - } - } - - } - private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo) { var eccWords = eccInfo.ECCPerBlock; @@ -1520,173 +1053,6 @@ private static List CreateCapacityTable() return localCapacityTable; } - /// - /// 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(byte[] codeWords, byte[] eccWords) - { - this.CodeWords = codeWords; - this.ECCWords = eccWords; - } - - public byte[] CodeWords { get; } - public byte[] ECCWords { 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 PolynomItem - { - public PolynomItem(int coefficient, int exponent) - { - this.Coefficient = coefficient; - this.Exponent = exponent; - } - - public int Coefficient { get; } - public int Exponent { get; } - } - - private struct Polynom - { - public Polynom(int count) - { - this.PolyItems = new List(count); - } - - public List PolyItems { get; set; } - - public override string ToString() - { - var sb = new StringBuilder(); - - foreach (var polyItem in this.PolyItems) - { - sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + "); - } - - if (sb.Length > 0) - sb.Length -= 3; - - return sb.ToString(); - } - } - - private readonly struct Point : IEquatable - { - public int X { get; } - public int Y { get; } - public Point(int x, int y) - { - this.X = x; - this.Y = y; - } - - public bool Equals(Point other) - { - return this.X == other.X && this.Y == other.Y; - } - - } - - private readonly struct 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 From ff0fd9ce999d7be976eb569e6e0436af137c0e41 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Fri, 3 May 2024 21:14:54 -0400 Subject: [PATCH 2/3] Extract MaskPattern to another file --- ...RCodeGenerator.ModulePlacer.MaskPattern.cs | 187 ++++++++++++++++++ QRCoder/QRCodeGenerator.ModulePlacer.cs | 179 +---------------- 2 files changed, 188 insertions(+), 178 deletions(-) create mode 100644 QRCoder/QRCodeGenerator.ModulePlacer.MaskPattern.cs diff --git a/QRCoder/QRCodeGenerator.ModulePlacer.MaskPattern.cs b/QRCoder/QRCodeGenerator.ModulePlacer.MaskPattern.cs new file mode 100644 index 00000000..895968f7 --- /dev/null +++ b/QRCoder/QRCodeGenerator.ModulePlacer.MaskPattern.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; + +namespace QRCoder +{ + public partial class QRCodeGenerator + { + private static partial class ModulePlacer + { + private static class MaskPattern + { + public static readonly Dictionary> Patterns = + 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 } + }; + + 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(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 + int blackModules = 0; + foreach (var bitArray in qrCode.ModuleMatrix) + for (var x = 0; x < size; x++) + if (bitArray[x]) + blackModules++; + + var percentDiv5 = blackModules * 20 / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count); + var prevMultipleOf5 = Math.Abs(percentDiv5 - 10); + var nextMultipleOf5 = Math.Abs(percentDiv5 - 9); + score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; + + return (score1 + score2) + (score3 + score4); + } + } + } + } +} diff --git a/QRCoder/QRCodeGenerator.ModulePlacer.cs b/QRCoder/QRCodeGenerator.ModulePlacer.cs index bbf5d2b9..69bea109 100644 --- a/QRCoder/QRCodeGenerator.ModulePlacer.cs +++ b/QRCoder/QRCodeGenerator.ModulePlacer.cs @@ -6,7 +6,7 @@ namespace QRCoder { public partial class QRCodeGenerator { - private static class ModulePlacer + private static partial class ModulePlacer { public static void AddQuietZone(QRCodeData qrCode) { @@ -293,183 +293,6 @@ private static bool IsBlocked(Rectangle r1, List blockedModules) } return false; } - - private static class MaskPattern - { - public static readonly Dictionary> Patterns = - 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 } - }; - - 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(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 - int blackModules = 0; - foreach (var bitArray in qrCode.ModuleMatrix) - for (var x = 0; x < size; x++) - if (bitArray[x]) - blackModules++; - - var percentDiv5 = blackModules * 20 / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count); - var prevMultipleOf5 = Math.Abs(percentDiv5 - 10); - var nextMultipleOf5 = Math.Abs(percentDiv5 - 9); - score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; - - return (score1 + score2) + (score3 + score4); - } - } - } } } From 307e6aaee86c3c9c69974b5398f23cdf820464a7 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Fri, 3 May 2024 23:17:04 -0400 Subject: [PATCH 3/3] Separate EciMode --- QRCoder/QRCodeGenerator.EciMode.cs | 13 +++++++++++++ QRCoder/QRCodeGenerator.cs | 8 -------- 2 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 QRCoder/QRCodeGenerator.EciMode.cs diff --git a/QRCoder/QRCodeGenerator.EciMode.cs b/QRCoder/QRCodeGenerator.EciMode.cs new file mode 100644 index 00000000..633731fc --- /dev/null +++ b/QRCoder/QRCodeGenerator.EciMode.cs @@ -0,0 +1,13 @@ +namespace QRCoder +{ + public partial class QRCodeGenerator + { + public enum EciMode + { + Default = 0, + Iso8859_1 = 3, + Iso8859_2 = 4, + Utf8 = 26 + } + } +} diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index e6841673..1cc1ef83 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -23,14 +23,6 @@ public partial class QRCodeGenerator : IDisposable private static readonly int[] galoisFieldByIntegerValue = { 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 }; private static readonly Dictionary alphanumEncDict = CreateAlphanumEncDict(); - public enum EciMode - { - Default = 0, - Iso8859_1 = 3, - Iso8859_2 = 4, - Utf8 = 26 - } - /// /// Initializes the QR code generator ///