Skip to content

Commit

Permalink
Merge pull request #528 from Shane32/reorganize_generate_qr_code
Browse files Browse the repository at this point in the history
Reorganize GenerateQrCode for better clarity
  • Loading branch information
codebude authored May 19, 2024
2 parents 1030701 + 308c231 commit 02baa95
Showing 1 changed file with 120 additions and 82 deletions.
202 changes: 120 additions & 82 deletions QRCoder/QRCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,109 +226,147 @@ private static ECCLevel ValidateECCLevel(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

0 comments on commit 02baa95

Please sign in to comment.