initital push
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
using System;
|
||||
|
||||
namespace Darkmatter.Fonepay
|
||||
{
|
||||
public static partial class FonepayQRGenerator
|
||||
{
|
||||
static bool[,] BuildMatrix(int version, byte[] codewords)
|
||||
{
|
||||
int size = version * 4 + 17;
|
||||
var matrix = new bool[size, size];
|
||||
var reserved = new bool[size, size];
|
||||
|
||||
PlaceFinder(matrix, reserved, 0, 0);
|
||||
PlaceFinder(matrix, reserved, 0, size - 7);
|
||||
PlaceFinder(matrix, reserved, size - 7, 0);
|
||||
PlaceTiming(matrix, reserved, size);
|
||||
PlaceDarkModule(matrix, reserved, version);
|
||||
if (version >= 2) PlaceAlignment(matrix, reserved, version);
|
||||
ReserveFormat(reserved, size);
|
||||
PlaceData(matrix, reserved, codewords, size);
|
||||
|
||||
int bestPenalty = int.MaxValue;
|
||||
bool[,] bestMatrix = null;
|
||||
for (int m = 0; m < 8; m++)
|
||||
{
|
||||
var candidate = (bool[,])matrix.Clone();
|
||||
ApplyMask(candidate, reserved, m, size);
|
||||
ApplyFormatInfo(candidate, reserved, 1, m, size);
|
||||
int penalty = CalcPenalty(candidate, size);
|
||||
if (penalty < bestPenalty)
|
||||
{
|
||||
bestPenalty = penalty;
|
||||
bestMatrix = (bool[,])candidate.Clone();
|
||||
}
|
||||
}
|
||||
return bestMatrix;
|
||||
}
|
||||
|
||||
static void PlaceFinder(bool[,] m, bool[,] r, int row, int col)
|
||||
{
|
||||
for (int dr = -1; dr <= 7; dr++)
|
||||
for (int dc = -1; dc <= 7; dc++)
|
||||
{
|
||||
int rr = row + dr, cc = col + dc;
|
||||
if (rr < 0 || cc < 0 || rr >= m.GetLength(0) || cc >= m.GetLength(1)) continue;
|
||||
r[rr, cc] = true;
|
||||
bool inOuter = dr >= 0 && dr <= 6 && dc >= 0 && dc <= 6;
|
||||
bool inBorder = (dr == 0 || dr == 6 || dc == 0 || dc == 6) && inOuter;
|
||||
bool inInner = dr >= 2 && dr <= 4 && dc >= 2 && dc <= 4;
|
||||
m[rr, cc] = !(dr == -1 || dc == -1 || dr == 7 || dc == 7) && (inBorder || inInner);
|
||||
}
|
||||
}
|
||||
|
||||
static void PlaceTiming(bool[,] m, bool[,] r, int size)
|
||||
{
|
||||
for (int i = 8; i < size - 8; i++)
|
||||
{
|
||||
bool v = i % 2 == 0;
|
||||
m[6, i] = v; r[6, i] = true;
|
||||
m[i, 6] = v; r[i, 6] = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void PlaceDarkModule(bool[,] m, bool[,] r, int ver)
|
||||
{
|
||||
int row = ver * 4 + 9;
|
||||
m[row, 8] = true;
|
||||
r[row, 8] = true;
|
||||
}
|
||||
|
||||
static readonly int[][] _alignCenters =
|
||||
{
|
||||
new int[] { },
|
||||
new[] { 6, 18 },
|
||||
new[] { 6, 22 },
|
||||
new[] { 6, 26 },
|
||||
new[] { 6, 30 },
|
||||
new[] { 6, 34 },
|
||||
new[] { 6, 22, 38 },
|
||||
new[] { 6, 24, 42 },
|
||||
new[] { 6, 26, 46 },
|
||||
new[] { 6, 28, 50 },
|
||||
};
|
||||
|
||||
static void PlaceAlignment(bool[,] m, bool[,] r, int version)
|
||||
{
|
||||
int[] centers = _alignCenters[version - 1];
|
||||
foreach (int row in centers)
|
||||
foreach (int col in centers)
|
||||
{
|
||||
if (r[row, col]) continue;
|
||||
for (int dr = -2; dr <= 2; dr++)
|
||||
for (int dc = -2; dc <= 2; dc++)
|
||||
{
|
||||
bool dark = Math.Abs(dr) == 2 || Math.Abs(dc) == 2 || (dr == 0 && dc == 0);
|
||||
m[row + dr, col + dc] = dark;
|
||||
r[row + dr, col + dc] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ReserveFormat(bool[,] r, int size)
|
||||
{
|
||||
for (int i = 0; i <= 8; i++)
|
||||
{
|
||||
r[8, i] = true;
|
||||
r[i, 8] = true;
|
||||
}
|
||||
for (int i = size - 8; i < size; i++)
|
||||
{
|
||||
r[8, i] = true;
|
||||
r[i, 8] = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void PlaceData(bool[,] m, bool[,] r, byte[] cw, int size)
|
||||
{
|
||||
int cwIdx = 0, bitIdx = 7;
|
||||
bool upward = true;
|
||||
int col = size - 1;
|
||||
|
||||
while (col > 0)
|
||||
{
|
||||
if (col == 6) col--;
|
||||
for (int rowStep = 0; rowStep < size; rowStep++)
|
||||
{
|
||||
int row = upward ? size - 1 - rowStep : rowStep;
|
||||
for (int c = 0; c < 2; c++)
|
||||
{
|
||||
int cc = col - c;
|
||||
if (r[row, cc]) continue;
|
||||
bool bit = cwIdx < cw.Length && ((cw[cwIdx] >> bitIdx) & 1) == 1;
|
||||
m[row, cc] = bit;
|
||||
if (--bitIdx < 0)
|
||||
{
|
||||
bitIdx = 7;
|
||||
cwIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
upward = !upward;
|
||||
col -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void ApplyMask(bool[,] m, bool[,] r, int mask, int size)
|
||||
{
|
||||
for (int row = 0; row < size; row++)
|
||||
for (int col = 0; col < size; col++)
|
||||
{
|
||||
if (r[row, col]) continue;
|
||||
bool flip = mask switch
|
||||
{
|
||||
0 => (row + col) % 2 == 0,
|
||||
1 => row % 2 == 0,
|
||||
2 => col % 3 == 0,
|
||||
3 => (row + col) % 3 == 0,
|
||||
4 => (row / 2 + col / 3) % 2 == 0,
|
||||
5 => row * col % 2 + row * col % 3 == 0,
|
||||
6 => (row * col % 2 + row * col % 3) % 2 == 0,
|
||||
7 => ((row + col) % 2 + row * col % 3) % 2 == 0,
|
||||
_ => false
|
||||
};
|
||||
if (flip) m[row, col] ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly ushort[] _formatL = { 0x77C4, 0x72F3, 0x7DAA, 0x789D, 0x662F, 0x6318, 0x6C41, 0x6976 };
|
||||
static readonly ushort[] _formatM = { 0x5412, 0x5125, 0x5E7C, 0x5B4B, 0x45F9, 0x40CE, 0x4F97, 0x4AA0 };
|
||||
static readonly ushort[] _formatQ = { 0x355F, 0x3068, 0x3F31, 0x3A06, 0x24B4, 0x2183, 0x2EDA, 0x2BED };
|
||||
static readonly ushort[] _formatH = { 0x1689, 0x13BE, 0x1CE7, 0x19D0, 0x0762, 0x0255, 0x0D0C, 0x083B };
|
||||
|
||||
static void ApplyFormatInfo(bool[,] m, bool[,] r, int eccIndex, int mask, int size)
|
||||
{
|
||||
ushort[] table = eccIndex switch { 0 => _formatM, 1 => _formatL, 2 => _formatH, _ => _formatQ };
|
||||
ushort fmt = table[mask];
|
||||
|
||||
int[] seq = { 0, 1, 2, 3, 4, 5, 7, 8 };
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bool bit = ((fmt >> (14 - i)) & 1) == 1;
|
||||
m[8, seq[i]] = bit;
|
||||
m[seq[i < 6 ? i : i + 1 < 8 ? i : i], 8] = bit;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
bool bit = ((fmt >> i) & 1) == 1;
|
||||
m[size - 1 - i, 8] = bit;
|
||||
}
|
||||
for (int i = 7; i < 15; i++)
|
||||
{
|
||||
bool bit = ((fmt >> i) & 1) == 1;
|
||||
m[8, size - 15 + i] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static int CalcPenalty(bool[,] m, int size)
|
||||
{
|
||||
int p = 0;
|
||||
for (int r = 0; r < size; r++)
|
||||
{
|
||||
int run = 1;
|
||||
for (int c = 1; c < size; c++)
|
||||
{
|
||||
if (m[r, c] == m[r, c - 1])
|
||||
{
|
||||
run++;
|
||||
if (run == 5) p += 3;
|
||||
else if (run > 5) p++;
|
||||
}
|
||||
else run = 1;
|
||||
}
|
||||
run = 1;
|
||||
for (int c = 1; c < size; c++)
|
||||
{
|
||||
if (m[c, r] == m[c - 1, r])
|
||||
{
|
||||
run++;
|
||||
if (run == 5) p += 3;
|
||||
else if (run > 5) p++;
|
||||
}
|
||||
else run = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int r = 0; r < size - 1; r++)
|
||||
for (int c = 0; c < size - 1; c++)
|
||||
if (m[r, c] == m[r, c + 1] && m[r, c] == m[r + 1, c] && m[r, c] == m[r + 1, c + 1])
|
||||
p += 3;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user