From 308c2319e9f34c050a2335dae0e1148e8b2fc3f8 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Sun, 19 May 2024 10:34:57 -0400 Subject: [PATCH] Reorganize GenerateQrCode for better clarity --- QRCoder/QRCodeGenerator.cs | 202 ++++++++++++++++++++++--------------- 1 file changed, 120 insertions(+), 82 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 43a69768..7766d0ce 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -200,109 +200,147 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) /// A QRCodeData structure containing the full QR code matrix, which can be used for rendering or analysis. private static QRCodeData GenerateQrCode(BitArray bitArray, 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 - bitArray.Length; - if (lengthDiff > 0) + + // Fill up data code word + PadData(); + + // Calculate error correction blocks + var codeWordWithECC = CalculateECCBlocks(); + + // Calculate interleaved code word lengths + var interleavedLength = CalculateInterleavedLength(); + + // Interleave code words + var interleavedData = InterleaveData(); + + // Place interleaved data on module matrix + var qrData = PlaceModules(); + + return qrData; + + + // fills the bit array with a repeating pattern to reach the required length + void PadData() { - // set 'write index' to end of existing bit array - var index = bitArray.Length; - // extend bit array to required length - bitArray.Length = dataLength; - // pad with 4 zeros (or less if lengthDiff < 4) - index += Math.Min(lengthDiff, 4); - // pad to nearest 8 bit boundary - if ((uint)index % 8 != 0) - index += 8 - (int)((uint)index % 8); - // pad with repeating pattern - var repeatingPatternIndex = 0; - while (index < dataLength) + var dataLength = eccInfo.TotalDataCodewords * 8; + var lengthDiff = dataLength - bitArray.Length; + if (lengthDiff > 0) { - bitArray[index++] = _repeatingPattern[repeatingPatternIndex++]; - if (repeatingPatternIndex >= _repeatingPattern.Length) - repeatingPatternIndex = 0; + // set 'write index' to end of existing bit array + var index = bitArray.Length; + // extend bit array to required length + bitArray.Length = dataLength; + // pad with 4 zeros (or less if lengthDiff < 4) + index += Math.Min(lengthDiff, 4); + // pad to nearest 8 bit boundary + if ((uint)index % 8 != 0) + index += 8 - (int)((uint)index % 8); + // pad with repeating pattern + var repeatingPatternIndex = 0; + while (index < dataLength) + { + bitArray[index++] = _repeatingPattern[repeatingPatternIndex++]; + if (repeatingPatternIndex >= _repeatingPattern.Length) + repeatingPatternIndex = 0; + } } } - // Generate the generator polynomial using the number of ECC words. - List codeWordWithECC; - using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock)) + List CalculateECCBlocks() { - //Calculate error correction words - codeWordWithECC = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); - AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, 0, bitArray.Length, generatorPolynom); - int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8; - AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, offset, bitArray.Length - offset, generatorPolynom); - } + List codewordBlocks; + // Generate the generator polynomial using the number of ECC words. + using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock)) + { + //Calculate error correction words + codewordBlocks = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); + AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, 0, bitArray.Length, generatorPolynom); + int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8; + AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, offset, bitArray.Length - offset, generatorPolynom); + return codewordBlocks; + } - //Calculate interleaved code word lengths - int interleavedLength = 0; - for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) - { - foreach (var codeBlock in codeWordWithECC) - if ((uint)codeBlock.CodeWordsLength / 8 > i) - interleavedLength += 8; - } - for (var i = 0; i < eccInfo.ECCPerBlock; i++) - { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.ECCWords.Length > i) - interleavedLength += 8; + void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, int offset2, int count, Polynom generatorPolynom) + { + var groupLength = codewordsInGroup * 8; + for (var i = 0; i < blocksInGroup; i++) + { + var eccWordList = CalculateECCWords(bitArray, offset2, groupLength, eccInfo, generatorPolynom); + codewordBlocks.Add(new CodewordBlock(offset2, groupLength, eccWordList)); + offset2 += groupLength; + } + } } - interleavedLength += remainderBits[version - 1]; - //Interleave code words - var interleavedData = new BitArray(interleavedLength); - int pos = 0; - for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) + // Calculate the length of the interleaved data + int CalculateInterleavedLength() { - foreach (var codeBlock in codeWordWithECC) + var length = 0; + for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) { - if ((uint)codeBlock.CodeWordsLength / 8 > i) - pos = bitArray.CopyTo(interleavedData, (int)((uint)i * 8) + codeBlock.CodeWordsOffset, pos, 8); + foreach (var codeBlock in codeWordWithECC) + if ((uint)codeBlock.CodeWordsLength / 8 > i) + length += 8; } - } - for (var i = 0; i < eccInfo.ECCPerBlock; i++) - { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.ECCWords.Length > i) - pos = DecToBin(codeBlock.ECCWords[i], 8, interleavedData, pos); + for (var i = 0; i < eccInfo.ECCPerBlock; i++) + { + foreach (var codeBlock in codeWordWithECC) + if (codeBlock.ECCWords.Length > i) + length += 8; + } + length += remainderBits[version - 1]; + return length; } - //Place interleaved data on module matrix - var qr = new QRCodeData(version); - var blockedModules = new List(17); - ModulePlacer.PlaceFinderPatterns(qr, blockedModules); - ModulePlacer.ReserveSeperatorAreas(qr.ModuleMatrix.Count, blockedModules); - ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules); - ModulePlacer.PlaceTimingPatterns(qr, blockedModules); - ModulePlacer.PlaceDarkModule(qr, version, blockedModules); - ModulePlacer.ReserveVersionAreas(qr.ModuleMatrix.Count, version, blockedModules); - ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules); - var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel); - var formatStr = GetFormatString(eccLevel, maskVersion); - - ModulePlacer.PlaceFormat(qr, formatStr); - if (version >= 7) + // Interleave the data + BitArray InterleaveData() { - var versionString = GetVersionString(version); - ModulePlacer.PlaceVersion(qr, versionString); - } - + var data = new BitArray(interleavedLength); + int pos = 0; + for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) + { + foreach (var codeBlock in codeWordWithECC) + { + if ((uint)codeBlock.CodeWordsLength / 8 > i) + pos = bitArray.CopyTo(data, (int)((uint)i * 8) + codeBlock.CodeWordsOffset, pos, 8); + } + } + for (var i = 0; i < eccInfo.ECCPerBlock; i++) + { + foreach (var codeBlock in codeWordWithECC) + if (codeBlock.ECCWords.Length > i) + pos = DecToBin(codeBlock.ECCWords[i], 8, data, pos); + } - ModulePlacer.AddQuietZone(qr); - return qr; + return data; + } - void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, int offset2, int count, Polynom generatorPolynom) + // Place the modules on the QR code matrix + QRCodeData PlaceModules() { - var groupLength = codewordsInGroup * 8; - for (var i = 0; i < blocksInGroup; i++) + var qr = new QRCodeData(version); + var blockedModules = new List(17); + ModulePlacer.PlaceFinderPatterns(qr, blockedModules); + ModulePlacer.ReserveSeperatorAreas(qr.ModuleMatrix.Count, blockedModules); + ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules); + ModulePlacer.PlaceTimingPatterns(qr, blockedModules); + ModulePlacer.PlaceDarkModule(qr, version, blockedModules); + ModulePlacer.ReserveVersionAreas(qr.ModuleMatrix.Count, version, blockedModules); + ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules); + var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel); + var formatStr = GetFormatString(eccLevel, maskVersion); + + ModulePlacer.PlaceFormat(qr, formatStr); + if (version >= 7) { - var eccWordList = CalculateECCWords(bitArray, offset2, groupLength, eccInfo, generatorPolynom); - codeWordWithECC.Add(new CodewordBlock(offset2, groupLength, eccWordList)); - offset2 += groupLength; + var versionString = GetVersionString(version); + ModulePlacer.PlaceVersion(qr, versionString); } + + ModulePlacer.AddQuietZone(qr); + + return qr; } }