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

Add code comments to QR code generation #517

Merged
merged 16 commits into from
May 6, 2024
12 changes: 12 additions & 0 deletions QRCoder/QRCodeGenerator.AlignmentPattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,21 @@ namespace QRCoder
{
public partial class QRCodeGenerator
{
/// <summary>
/// Represents the alignment pattern used in QR codes, which helps ensure the code remains readable even if it is somewhat distorted.
/// Each QR code version has its own specific alignment pattern locations which this struct encapsulates.
/// </summary>
private struct AlignmentPattern
{
/// <summary>
/// The version of the QR code. Higher versions have more complex and numerous alignment patterns.
/// </summary>
public int Version;

/// <summary>
/// A list of points where alignment patterns are located within the QR code matrix.
/// Each point represents the center of an alignment pattern.
/// </summary>
public List<Point> PatternPositions;
}
}
Expand Down
16 changes: 16 additions & 0 deletions QRCoder/QRCodeGenerator.CodewordBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,31 @@
{
public partial class QRCodeGenerator
{
/// <summary>
/// Represents a block of codewords in a QR code. QR codes are divided into several blocks for error correction purposes.
/// Each block contains a series of data codewords followed by error correction codewords.
/// </summary>
private struct CodewordBlock
{
/// <summary>
/// Initializes a new instance of the CodewordBlock struct with specified arrays of code words and error correction (ECC) words.
/// </summary>
/// <param name="codeWords">The array of data codewords for this block. Data codewords carry the actual information.</param>
/// <param name="eccWords">The array of error correction codewords for this block. These codewords help recover the data if the QR code is damaged.</param>
public CodewordBlock(byte[] codeWords, byte[] eccWords)
{
this.CodeWords = codeWords;
this.ECCWords = eccWords;
}

/// <summary>
/// Gets the data codewords associated with this block.
/// </summary>
public byte[] CodeWords { get; }

/// <summary>
/// Gets the error correction codewords associated with this block.
/// </summary>
public byte[] ECCWords { get; }
}
}
Expand Down
46 changes: 46 additions & 0 deletions QRCoder/QRCodeGenerator.ECCInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,22 @@
{
public partial class QRCodeGenerator
{
/// <summary>
/// Represents the error correction coding (ECC) information for a specific version and error correction level of a QR code.
/// </summary>
private struct ECCInfo
{
/// <summary>
/// Initializes a new instance of the ECCInfo struct with specified properties.
/// </summary>
/// <param name="version">The version number of the QR code.</param>
/// <param name="errorCorrectionLevel">The error correction level used in the QR code.</param>
/// <param name="totalDataCodewords">The total number of data codewords for this version and error correction level.</param>
/// <param name="eccPerBlock">The number of error correction codewords per block.</param>
/// <param name="blocksInGroup1">The number of blocks in group 1.</param>
/// <param name="codewordsInGroup1">The number of codewords in each block of group 1.</param>
/// <param name="blocksInGroup2">The number of blocks in group 2, if any.</param>
/// <param name="codewordsInGroup2">The number of codewords in each block of group 2, if any.</param>
public ECCInfo(int version, ECCLevel errorCorrectionLevel, int totalDataCodewords, int eccPerBlock, int blocksInGroup1,
int codewordsInGroup1, int blocksInGroup2, int codewordsInGroup2)
{
Expand All @@ -16,13 +30,45 @@ public ECCInfo(int version, ECCLevel errorCorrectionLevel, int totalDataCodeword
this.BlocksInGroup2 = blocksInGroup2;
this.CodewordsInGroup2 = codewordsInGroup2;
}

/// <summary>
/// Gets the version number of the QR code.
/// </summary>
public int Version { get; }

/// <summary>
/// Gets the error correction level of the QR code.
/// </summary>
public ECCLevel ErrorCorrectionLevel { get; }

/// <summary>
/// Gets the total number of data codewords for this version and error correction level.
/// </summary>
public int TotalDataCodewords { get; }

/// <summary>
/// Gets the number of error correction codewords per block.
/// </summary>
public int ECCPerBlock { get; }

/// <summary>
/// Gets the number of blocks in group 1.
/// </summary>
public int BlocksInGroup1 { get; }

/// <summary>
/// Gets the number of codewords in each block of group 1.
/// </summary>
public int CodewordsInGroup1 { get; }

/// <summary>
/// Gets the number of blocks in group 2, if any.
/// </summary>
public int BlocksInGroup2 { get; }

/// <summary>
/// Gets the number of codewords in each block of group 2, if any.
/// </summary>
public int CodewordsInGroup2 { get; }
}
}
Expand Down
23 changes: 23 additions & 0 deletions QRCoder/QRCodeGenerator.EciMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,34 @@
{
public partial class QRCodeGenerator
{
/// <summary>
/// Enumerates the Extended Channel Interpretation (ECI) modes used in QR codes to handle different character encoding standards.
/// ECI mode allows QR codes to efficiently encode data using character sets other than the default ISO-8859-1.
/// </summary>
public enum EciMode
{
/// <summary>
/// Default encoding mode (typically ISO-8859-1). Used when no ECI mode is explicitly specified.
/// This mode is assumed in basic QR codes where no extended character sets are needed.
/// </summary>
Default = 0,

/// <summary>
/// Specifies the use of the ISO-8859-1 character set, covering most Western European languages.
/// This mode explicitly sets the encoding to ISO-8859-1, which includes characters used in languages such as English, French, German, and Spanish.
/// </summary>
Iso8859_1 = 3,

/// <summary>
/// Specifies the use of the ISO-8859-2 character set, which is primarily used for Central and Eastern European languages.
/// This includes characters used in languages such as Polish, Czech, Slovak, Hungarian, and Romanian.
/// </summary>
Iso8859_2 = 4,

/// <summary>
/// Specifies the use of UTF-8 encoding.
/// UTF-8 can encode any Unicode character and is useful for QR codes that need to support multi-language content.
/// </summary>
Utf8 = 26
}
}
Expand Down
27 changes: 27 additions & 0 deletions QRCoder/QRCodeGenerator.EncodingMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,39 @@
{
public partial class QRCodeGenerator
{
/// <summary>
/// Specifies the encoding modes for the characters in a QR code.
/// </summary>
private enum EncodingMode
{
/// <summary>
/// Numeric encoding mode, which is used to encode numeric data (digits 0-9).
/// Three characters are encoded into 10 bits.
/// </summary>
Numeric = 1,

/// <summary>
/// Alphanumeric encoding mode, which is used to encode alphanumeric characters (0-9, A-Z, space, and some punctuation).
/// Two characters are encoded into 11 bits.
/// </summary>
Alphanumeric = 2,

/// <summary>
/// Byte encoding mode, primarily using the ISO-8859-1 character set. Each character is encoded into 8 bits.
/// When combined with ECI, it can be adapted to use other character sets.
/// </summary>
Byte = 4,

/// <summary>
/// Kanji encoding mode, which is used to encode characters from the Shift JIS character set, primarily for Japanese Kanji and Kana characters.
/// One character is encoded into 13 bits.
/// </summary>
Kanji = 8,
codebude marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Extended Channel Interpretation (ECI) mode, which specifies a character set via an 8-bit number followed by one of the other encoding modes.
/// This allows adapting the byte encoding to accommodate various global text encodings.
/// </summary>
ECI = 7
}
}
Expand Down
70 changes: 60 additions & 10 deletions QRCoder/QRCodeGenerator.ModulePlacer.MaskPattern.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;

namespace QRCoder
Expand All @@ -7,63 +8,109 @@ public partial class QRCodeGenerator
{
private static partial class ModulePlacer
{
/// <summary>
/// Provides static methods and properties to handle mask patterns used in QR code generation.
/// Mask patterns are applied to QR codes to break up patterns in the data matrix that might confuse scanners.
/// </summary>
private static class MaskPattern
{
/// <summary>
/// A dictionary mapping each mask pattern index to its corresponding function that calculates whether a given pixel should be masked.
/// </summary>
public static readonly Dictionary<int, Func<int, int, bool>> Patterns =
new Dictionary<int, Func<int, int, bool>>(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 }
};

/// <summary>
/// Mask pattern 1: (x + y) % 2 == 0
/// Applies a checkerboard mask on the QR code.
/// </summary>
public static bool Pattern1(int x, int y)
{
return (x + y) % 2 == 0;
}

/// <summary>
/// Mask pattern 2: y % 2 == 0
/// Applies a horizontal striping mask on the QR code.
/// </summary>
public static bool Pattern2(int x, int y)
{
return y % 2 == 0;
}

/// <summary>
/// Mask pattern 3: x % 3 == 0
/// Applies a vertical striping mask on the QR code.
/// </summary>
public static bool Pattern3(int x, int y)
{
return x % 3 == 0;
}

/// <summary>
/// Mask pattern 4: (x + y) % 3 == 0
/// Applies a diagonal striping mask on the QR code.
/// </summary>
public static bool Pattern4(int x, int y)
{
return (x + y) % 3 == 0;
}

/// <summary>
/// Mask pattern 5: ((y / 2) + (x / 3)) % 2 == 0
/// Applies a complex pattern mask on the QR code, mixing horizontal and vertical rules.
/// </summary>
public static bool Pattern5(int x, int y)
{
return ((int)(Math.Floor(y / 2d) + Math.Floor(x / 3d)) % 2) == 0;
}

/// <summary>
/// Mask pattern 6: ((x * y) % 2 + (x * y) % 3) == 0
/// Applies a mask based on the product of x and y coordinates modulo 2 and 3.
/// </summary>
public static bool Pattern6(int x, int y)
{
return ((x * y) % 2) + ((x * y) % 3) == 0;
}

/// <summary>
/// Mask pattern 7: (((x * y) % 2 + (x * y) % 3) % 2) == 0
/// Applies a mask based on a more complex function involving the product of x and y coordinates.
/// </summary>
public static bool Pattern7(int x, int y)
{
return (((x * y) % 2) + ((x * y) % 3)) % 2 == 0;
}

/// <summary>
/// Mask pattern 8: (((x + y) % 2) + ((x * y) % 3) % 2) == 0
/// Combines rules of checkers and complex multiplicative masks.
/// </summary>
public static bool Pattern8(int x, int y)
{
return (((x + y) % 2) + ((x * y) % 3)) % 2 == 0;
}

/// <summary>
/// Calculates a penalty score for a QR code to evaluate the effectiveness of a mask pattern.
/// A lower score indicates a QR code that is easier for decoders to read accurately.
/// The score is the sum of four penalty rules applied to the QR code.
/// </summary>
/// <param name="qrCode">The QR code data structure to be evaluated.</param>
/// <returns>The total penalty score of the QR code.</returns>
public static int Score(QRCodeData qrCode)
{
int score1 = 0,
score2 = 0,
score3 = 0,
score4 = 0;
int score1 = 0, // Penalty for groups of five or more same-color modules in a row (or column)
score2 = 0, // Penalty for blocks of modules in the same color
score3 = 0, // Penalty for specific patterns found within the QR code
score4 = 0; // Penalty for having more than 50% black modules or more than 50% white modules
var size = qrCode.ModuleMatrix.Count;

//Penalty 1
//Penalty 1: Checking for consecutive modules of the same color in rows and columns
for (var y = 0; y < size; y++)
{
var modInRow = 0;
Expand All @@ -72,6 +119,7 @@ public static int Score(QRCodeData qrCode)
var lastValColumn = qrCode.ModuleMatrix[0][y];
for (var x = 0; x < size; x++)
{
// Check rows for consecutive modules
if (qrCode.ModuleMatrix[y][x] == lastValRow)
modInRow++;
else
Expand All @@ -82,7 +130,7 @@ public static int Score(QRCodeData qrCode)
score1++;
lastValRow = qrCode.ModuleMatrix[y][x];


// Check columns for consecutive modules
if (qrCode.ModuleMatrix[x][y] == lastValColumn)
modInColumn++;
else
Expand All @@ -95,8 +143,7 @@ public static int Score(QRCodeData qrCode)
}
}


//Penalty 2
//Penalty 2: Checking for blocks of modules in the same color
for (var y = 0; y < size - 1; y++)
{
for (var x = 0; x < size - 1; x++)
Expand All @@ -108,11 +155,12 @@ public static int Score(QRCodeData qrCode)
}
}

//Penalty 3
//Penalty 3: Checking for specific patterns within the QR code (patterns that should be avoided)
for (var y = 0; y < size; y++)
{
for (var x = 0; x < size - 10; x++)
{
// Horizontal pattern matching
if ((qrCode.ModuleMatrix[y][x] &&
!qrCode.ModuleMatrix[y][x + 1] &&
qrCode.ModuleMatrix[y][x + 2] &&
Expand All @@ -139,6 +187,7 @@ public static int Score(QRCodeData qrCode)
score3 += 40;
}

// Vertical pattern matching
if ((qrCode.ModuleMatrix[x][y] &&
!qrCode.ModuleMatrix[x + 1][y] &&
qrCode.ModuleMatrix[x + 2][y] &&
Expand Down Expand Up @@ -167,7 +216,7 @@ public static int Score(QRCodeData qrCode)
}
}

//Penalty 4
//Penalty 4: Proportions of dark and light modules
int blackModules = 0;
foreach (var bitArray in qrCode.ModuleMatrix)
for (var x = 0; x < size; x++)
Expand All @@ -179,6 +228,7 @@ public static int Score(QRCodeData qrCode)
var nextMultipleOf5 = Math.Abs(percentDiv5 - 9);
score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10;

// Return the sum of all four penalties
return (score1 + score2) + (score3 + score4);
}
}
Expand Down
Loading