Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorganize GenerateQrCode for better clarity #528

Merged
merged 1 commit into from
May 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 120 additions & 82 deletions QRCoder/QRCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,109 200,147 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
/// <returns>A QRCodeData structure containing the full QR code matrix, which can be used for rendering or analysis.</returns>
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<CodewordBlock> codeWordWithECC;
using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock))
List<CodewordBlock> CalculateECCBlocks()
{
//Calculate error correction words
codeWordWithECC = new List<CodewordBlock>(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<CodewordBlock> codewordBlocks;
// Generate the generator polynomial using the number of ECC words.
using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock))
{
//Calculate error correction words
codewordBlocks = new List<CodewordBlock>(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<Rectangle>(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<Rectangle>(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;
}
}

Expand Down