diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs
index a0c02dc7..ff1e215c 100644
--- a/QRCoder/QRCodeGenerator.cs
+++ b/QRCoder/QRCodeGenerator.cs
@@ -1,10 +1,8 @@
#if HAS_SPAN
using System.Buffers;
#endif
-using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
-using System.Text;
namespace QRCoder;
@@ -458,7 +456,7 @@ QRCodeData PlaceModules()
{
ModulePlacer.PlaceFinderPatterns(qr, blockedModules);
ModulePlacer.ReserveSeperatorAreas(version, size, blockedModules);
- ModulePlacer.PlaceAlignmentPatterns(qr, AlignmentPatterns.FromVersion(version).PatternPositions, blockedModules);
+ ModulePlacer.PlaceAlignmentPatterns(qr, AlignmentPatterns.FromVersion(version), blockedModules);
ModulePlacer.PlaceTimingPatterns(qr, blockedModules);
ModulePlacer.PlaceDarkModule(qr, version, blockedModules);
ModulePlacer.ReserveVersionAreas(size, version, blockedModules);
diff --git a/QRCoder/QRCodeGenerator/AlignmentPattern.cs b/QRCoder/QRCodeGenerator/AlignmentPattern.cs
deleted file mode 100644
index cc795367..00000000
--- a/QRCoder/QRCodeGenerator/AlignmentPattern.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace QRCoder;
-
-public partial class QRCodeGenerator
-{
- ///
- /// 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.
- ///
- private struct AlignmentPattern
- {
- ///
- /// The version of the QR code. Higher versions have more complex and numerous alignment patterns.
- ///
- public int Version;
-
- ///
- /// A list of points where alignment patterns are located within the QR code matrix.
- /// Each point represents the center of an alignment pattern.
- ///
- public List PatternPositions;
- }
-}
diff --git a/QRCoder/QRCodeGenerator/AlignmentPatterns.cs b/QRCoder/QRCodeGenerator/AlignmentPatterns.cs
index 8b09a1a5..81a2f02d 100644
--- a/QRCoder/QRCodeGenerator/AlignmentPatterns.cs
+++ b/QRCoder/QRCodeGenerator/AlignmentPatterns.cs
@@ -1,3 +1,5 @@
+using System.Diagnostics;
+
namespace QRCoder;
public partial class QRCodeGenerator
@@ -10,57 +12,69 @@ private static class AlignmentPatterns
///
/// A lookup table mapping QR code versions to their corresponding alignment patterns.
///
- private static readonly Dictionary _alignmentPatternTable = CreateAlignmentPatternTable();
+ ///
+ /// Versions range from -4 up to and including -1 and 1 up to and including 40.
+ ///
+ private static readonly Point[][] _alignmentPatternTable = CreateAlignmentPatternTable();
+
+ ///
+ /// Offset used to map -4 .. 40 to 0 .. 44
+ ///
+ private const int INDEX_OFFSET = 4;
///
/// Retrieves the alignment pattern for a specific QR code version.
///
- public static AlignmentPattern FromVersion(int version) => _alignmentPatternTable[version];
+ public static Point[] FromVersion(int version) => _alignmentPatternTable[version + INDEX_OFFSET];
///
/// Creates a lookup table mapping QR code versions to their corresponding alignment patterns.
/// Alignment patterns are used in QR codes to help scanners accurately read the code at high speeds and when partially obscured.
/// This table provides the necessary patterns based on the QR code version which dictates the size and complexity of the QR code.
///
- /// A dictionary where keys are QR code version numbers and values are AlignmentPattern structures detailing the positions of alignment patterns for each version.
- private static Dictionary CreateAlignmentPatternTable()
+ /// An array where indices are QR code version numbers offset by 4 and values are Point arrays containing the positions of alignment patterns for each version.
+ private static Point[][] CreateAlignmentPatternTable()
{
- var alignmentPatternBaseValues = new int[] { 0, 0, 0, 0, 0, 0, 0, 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, 0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, 0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, 80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, 82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, 158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 };
- var localAlignmentPatternTable = new Dictionary(40 + 4);
+ var localAlignmentPatternTable = new Point[4 + 1 + 40][];
+
+ // Micro QR codes do not have alignment patterns.
+ Point[] empty = [];
+
+ localAlignmentPatternTable[-4 + INDEX_OFFSET] = empty;
+ localAlignmentPatternTable[-3 + INDEX_OFFSET] = empty;
+ localAlignmentPatternTable[-2 + INDEX_OFFSET] = empty;
+ localAlignmentPatternTable[-1 + INDEX_OFFSET] = empty;
+
+ // A Version 1 QR code does not have alignment patterns.
+ localAlignmentPatternTable[1 + INDEX_OFFSET] = empty;
- for (var i = 0; i < (7 * 40); i += 7)
+#if HAS_SPAN
+ ReadOnlySpan alignmentPatternBaseValues =
+#else
+ byte[] alignmentPatternBaseValues =
+#endif
+ [4, 16, 0, 0, 0, 0, 0, 4, 20, 0, 0, 0, 0, 0, 4, 24, 0, 0, 0, 0, 0, 4, 28, 0, 0, 0, 0, 0, 4, 32, 0, 0, 0, 0, 0, 4, 20, 36, 0, 0, 0, 0, 4, 22, 40, 0, 0, 0, 0, 4, 24, 44, 0, 0, 0, 0, 4, 26, 48, 0, 0, 0, 0, 4, 28, 52, 0, 0, 0, 0, 4, 30, 56, 0, 0, 0, 0, 4, 32, 60, 0, 0, 0, 0, 4, 24, 44, 64, 0, 0, 0, 4, 24, 46, 68, 0, 0, 0, 4, 24, 48, 72, 0, 0, 0, 4, 28, 52, 76, 0, 0, 0, 4, 28, 54, 80, 0, 0, 0, 4, 28, 56, 84, 0, 0, 0, 4, 32, 60, 88, 0, 0, 0, 4, 26, 48, 70, 92, 0, 0, 4, 24, 48, 72, 96, 0, 0, 4, 28, 52, 76, 100, 0, 0, 4, 26, 52, 78, 104, 0, 0, 4, 30, 56, 82, 108, 0, 0, 4, 28, 56, 84, 112, 0, 0, 4, 32, 60, 88, 116, 0, 0, 4, 24, 48, 72, 96, 120, 0, 4, 28, 52, 76, 100, 124, 0, 4, 24, 50, 76, 102, 128, 0, 4, 28, 54, 80, 106, 132, 0, 4, 32, 58, 84, 110, 136, 0, 4, 28, 56, 84, 112, 140, 0, 4, 32, 60, 88, 116, 144, 0, 4, 28, 52, 76, 100, 124, 148, 4, 22, 48, 74, 100, 126, 152, 4, 26, 52, 78, 104, 130, 156, 4, 30, 56, 82, 108, 134, 160, 4, 24, 52, 80, 108, 136, 164, 4, 28, 56, 84, 112, 140, 168];
+
+ var points = new List(7 * 7);
+
+ for (var version = 2; version <= 40; version++)
{
- var points = new List(50);
- for (var x = 0; x < 7; x++)
+ var indexBase = (version - 2) * 7;
+
+ for (var x = 0; x < 7 && alignmentPatternBaseValues[indexBase + x] != 0; x++)
{
- if (alignmentPatternBaseValues[i + x] != 0)
+ for (var y = 0; y < 7 && alignmentPatternBaseValues[indexBase + y] != 0; y++)
{
- for (var y = 0; y < 7; y++)
- {
- if (alignmentPatternBaseValues[i + y] != 0)
- {
- var p = new Point(alignmentPatternBaseValues[i + x] - 2, alignmentPatternBaseValues[i + y] - 2);
- if (!points.Contains(p))
- points.Add(p);
- }
- }
+ points.Add(new Point(alignmentPatternBaseValues[indexBase + x], alignmentPatternBaseValues[indexBase + y]));
}
}
- var version = (i + 7) / 7;
- localAlignmentPatternTable.Add(version, new AlignmentPattern()
- {
- Version = version,
- PatternPositions = points
- });
- }
+ Debug.Assert(points.Count <= 7 * 7);
- // Micro QR codes do not have alignment patterns.
- var emptyPointList = new List();
- localAlignmentPatternTable.Add(-1, new AlignmentPattern { Version = -1, PatternPositions = emptyPointList });
- localAlignmentPatternTable.Add(-2, new AlignmentPattern { Version = -2, PatternPositions = emptyPointList });
- localAlignmentPatternTable.Add(-3, new AlignmentPattern { Version = -3, PatternPositions = emptyPointList });
- localAlignmentPatternTable.Add(-4, new AlignmentPattern { Version = -4, PatternPositions = emptyPointList });
+ localAlignmentPatternTable[version + INDEX_OFFSET] = points.ToArray();
+
+ points.Clear();
+ }
return localAlignmentPatternTable;
}
diff --git a/QRCoder/QRCodeGenerator/ModulePlacer.cs b/QRCoder/QRCodeGenerator/ModulePlacer.cs
index 989c7049..771cd8ae 100644
--- a/QRCoder/QRCodeGenerator/ModulePlacer.cs
+++ b/QRCoder/QRCodeGenerator/ModulePlacer.cs
@@ -362,7 +362,7 @@ public static void PlaceFinderPatterns(QRCodeData qrCode, BlockedModules blocked
/// The QR code data structure where the alignment patterns will be placed.
/// A list of points representing the centers of where alignment patterns should be placed.
/// A list of rectangles representing areas that must not be overwritten. Updated with the areas occupied by alignment patterns.
- public static void PlaceAlignmentPatterns(QRCodeData qrCode, List alignmentPatternLocations, BlockedModules blockedModules)
+ public static void PlaceAlignmentPatterns(QRCodeData qrCode, Point[] alignmentPatternLocations, BlockedModules blockedModules)
{
// Iterate through each specified location for alignment patterns.
foreach (var loc in alignmentPatternLocations)
@@ -377,22 +377,36 @@ public static void PlaceAlignmentPatterns(QRCodeData qrCode, List alignme
continue;
}
+ // Add the alignment pattern's area to the list of blocked modules to prevent future overwrites.
+ blockedModules.Add(alignmentPatternRect);
+
// Place the alignment pattern by setting modules within the 5x5 area.
// The pattern consists of a 3x3 center block with a single module border.
- for (var x = 0; x < 5; x++)
+ // Create the pattern: a 3x3 block surrounded by a border, with the very center module set.
+ for (var y = 0; y < 5; y++)
{
- for (var y = 0; y < 5; y++)
+ var moduleRow = qrCode.ModuleMatrix[loc.Y + 4 + y];
+
+ switch (y)
{
- // Create the pattern: a 3x3 block surrounded by a border, with the very center module set.
- if (y == 0 || y == 4 || x == 0 || x == 4 || (x == 2 && y == 2))
- {
- qrCode.ModuleMatrix[loc.Y + y + 4][loc.X + x + 4] = true;
- }
+ case 0 or 4:
+ moduleRow[loc.X + 4 + 0] = true;
+ moduleRow[loc.X + 4 + 1] = true;
+ moduleRow[loc.X + 4 + 2] = true;
+ moduleRow[loc.X + 4 + 3] = true;
+ moduleRow[loc.X + 4 + 4] = true;
+ break;
+ case 1 or 3:
+ moduleRow[loc.X + 4 + 0] = true;
+ moduleRow[loc.X + 4 + 4] = true;
+ break;
+ case 2:
+ moduleRow[loc.X + 4 + 0] = true;
+ moduleRow[loc.X + 4 + 2] = true;
+ moduleRow[loc.X + 4 + 4] = true;
+ break;
}
}
-
- // Add the alignment pattern's area to the list of blocked modules to prevent future overwrites.
- blockedModules.Add(new Rectangle(loc.X, loc.Y, 5, 5));
}
}
diff --git a/QRCoder/QRCodeGenerator/Point.cs b/QRCoder/QRCodeGenerator/Point.cs
index 6ec9d532..cdd6b4ad 100644
--- a/QRCoder/QRCodeGenerator/Point.cs
+++ b/QRCoder/QRCodeGenerator/Point.cs
@@ -1,56 +1,31 @@
-using System.Reflection;
-
namespace QRCoder;
public partial class QRCodeGenerator
{
///
- /// Represents a 2D point with integer coordinates.
+ /// Represents a 2D point with byte coordinates.
///
- private readonly struct Point : IEquatable
+ private readonly struct Point
{
///
/// Gets the X-coordinate of the point.
///
- public int X { get; }
+ public byte X { get; }
///
/// Gets the Y-coordinate of the point.
///
- public int Y { get; }
+ public byte Y { get; }
///
/// Initializes a new instance of the struct with specified X and Y coordinates.
///
/// The X-coordinate of the point.
/// The Y-coordinate of the point.
- public Point(int x, int y)
+ public Point(byte x, byte y)
{
X = x;
Y = y;
}
-
- ///
- /// Determines whether the specified is equal to the current .
- ///
- /// The to compare with the current .
- /// True if the specified has the same X and Y coordinates as the current ; otherwise, false.
- ///
- /// If this method which implements is not implemented, comparisons used by methods such as
- /// fall back to reflection, which causes heap allocations internally during the calls to .
- ///
- public bool Equals(Point other)
- => X == other.X && Y == other.Y;
-
- ///
- public override bool Equals(object? obj) => obj is Point point && Equals(point);
-
- ///
- public override int GetHashCode()
-#if NET5_0_OR_GREATER
- => HashCode.Combine(X, Y);
-#else
- => X ^ (int)(((uint)Y << 16) | ((uint)Y >> 16));
-#endif
}
}