484 lines
13 KiB
C#
484 lines
13 KiB
C#
using System;
|
|
using QRCodeDecoder = ThoughtWorks.QRCode.Codec.QRCodeDecoder;
|
|
using BCH15_5 = ThoughtWorks.QRCode.Codec.Ecc.BCH15_5;
|
|
using ThoughtWorks.QRCode.Geom;
|
|
using LogicalSeed = ThoughtWorks.QRCode.Codec.Reader.Pattern.LogicalSeed;
|
|
using DebugCanvas = ThoughtWorks.QRCode.Codec.Util.DebugCanvas;
|
|
using SystemUtils = ThoughtWorks.QRCode.Codec.Util.SystemUtils;
|
|
|
|
namespace ThoughtWorks.QRCode.Codec.Data
|
|
{
|
|
|
|
public class QRCodeSymbol
|
|
{
|
|
internal int version;
|
|
internal int errorCollectionLevel;
|
|
internal int maskPattern;
|
|
internal int dataCapacity;
|
|
internal bool[][] moduleMatrix;
|
|
internal int width, height;
|
|
internal Point[][] alignmentPattern;
|
|
internal int[][] numErrorCollectionCode = new int[][] { new int[] { 7, 10, 13, 17 }, new int[] { 10, 16, 22, 28 }, new int[] { 15, 26, 36, 44 }, new int[] { 20, 36, 52, 64 }, new int[] { 26, 48, 72, 88 }, new int[] { 36, 64, 96, 112 }, new int[] { 40, 72, 108, 130 }, new int[] { 48, 88, 132, 156 }, new int[] { 60, 110, 160, 192 }, new int[] { 72, 130, 192, 224 }, new int[] { 80, 150, 224, 264 }, new int[] { 96, 176, 260, 308 }, new int[] { 104, 198, 288, 352 }, new int[] { 120, 216, 320, 384 }, new int[] { 132, 240, 360, 432 }, new int[] { 144, 280, 408, 480 }, new int[] { 168, 308, 448, 532 }, new int[] { 180, 338, 504, 588 }, new int[] { 196, 364, 546, 650 }, new int[] { 224, 416, 600, 700 }, new int[] { 224, 442, 644, 750 }, new int[] { 252, 476, 690, 816 }, new int[] { 270, 504, 750, 900 }, new int[] { 300, 560, 810, 960 }, new int[] { 312, 588, 870, 1050 }, new int[] { 336, 644, 952, 1110 }, new int[] { 360, 700, 1020, 1200 }, new int[] { 390, 728, 1050, 1260 }, new int[] { 420, 784, 1140, 1350 }, new int[] { 450, 812, 1200, 1440 }, new int[] { 480, 868, 1290, 1530 }, new int[] { 510, 924, 1350, 1620 }, new int[] { 540, 980, 1440, 1710 }, new int[] { 570, 1036, 1530, 1800 }, new int[] { 570, 1064, 1590, 1890 }, new int[] { 600, 1120, 1680, 1980 }, new int[] { 630, 1204, 1770, 2100 }, new int[] { 660, 1260, 1860, 2220 }, new int[] { 720, 1316, 1950, 2310 }, new int[] { 750, 1372, 2040, 2430 } };
|
|
internal int[][] numRSBlocks = new int[][] { new int[] { 1, 1, 1, 1 }, new int[] { 1, 1, 1, 1 }, new int[] { 1, 1, 2, 2 }, new int[] { 1, 2, 2, 4 }, new int[] { 1, 2, 4, 4 }, new int[] { 2, 4, 4, 4 }, new int[] { 2, 4, 6, 5 }, new int[] { 2, 4, 6, 6 }, new int[] { 2, 5, 8, 8 }, new int[] { 4, 5, 8, 8 }, new int[] { 4, 5, 8, 11 }, new int[] { 4, 8, 10, 11 }, new int[] { 4, 9, 12, 16 }, new int[] { 4, 9, 16, 16 }, new int[] { 6, 10, 12, 18 }, new int[] { 6, 10, 17, 16 }, new int[] { 6, 11, 16, 19 }, new int[] { 6, 13, 18, 21 }, new int[] { 7, 14, 21, 25 }, new int[] { 8, 16, 20, 25 }, new int[] { 8, 17, 23, 25 }, new int[] { 9, 17, 23, 34 }, new int[] { 9, 18, 25, 30 }, new int[] { 10, 20, 27, 32 }, new int[] { 12, 21, 29, 35 }, new int[] { 12, 23, 34, 37 }, new int[] { 12, 25, 34, 40 }, new int[] { 13, 26, 35, 42 }, new int[] { 14, 28, 38, 45 }, new int[] { 15, 29, 40, 48 }, new int[] { 16, 31, 43, 51 }, new int[] { 17, 33, 45, 54 }, new int[] { 18, 35, 48, 57 }, new int[] { 19, 37, 51, 60 }, new int[] { 19, 38, 53, 63 }, new int[] { 20, 40, 56, 66 }, new int[] { 21, 43, 59, 70 }, new int[] { 22, 45, 62, 74 }, new int[] { 24, 47, 65, 77 }, new int[] { 25, 49, 68, 81 } };
|
|
|
|
virtual public int NumErrorCollectionCode
|
|
{
|
|
get
|
|
{
|
|
return numErrorCollectionCode[version - 1][errorCollectionLevel];
|
|
}
|
|
|
|
}
|
|
virtual public int NumRSBlocks
|
|
{
|
|
get
|
|
{
|
|
return numRSBlocks[version - 1][errorCollectionLevel];
|
|
}
|
|
|
|
}
|
|
virtual public int Version
|
|
{
|
|
get
|
|
{
|
|
return version;
|
|
}
|
|
|
|
}
|
|
virtual public String VersionReference
|
|
{
|
|
get
|
|
{
|
|
char[] versionReferenceCharacter = new char[]{'L', 'M', 'Q', 'H'};
|
|
return System.Convert.ToString(version) + "-" + versionReferenceCharacter[errorCollectionLevel];
|
|
}
|
|
|
|
}
|
|
virtual public Point[][] AlignmentPattern
|
|
{
|
|
get
|
|
{
|
|
return alignmentPattern;
|
|
}
|
|
|
|
}
|
|
virtual public int DataCapacity
|
|
{
|
|
get
|
|
{
|
|
return this.dataCapacity;
|
|
}
|
|
|
|
}
|
|
virtual public int ErrorCollectionLevel
|
|
{
|
|
get
|
|
{
|
|
return errorCollectionLevel;
|
|
}
|
|
|
|
}
|
|
virtual public int MaskPatternReferer
|
|
{
|
|
get
|
|
{
|
|
return maskPattern;
|
|
}
|
|
|
|
}
|
|
virtual public String MaskPatternRefererAsString
|
|
{
|
|
// for debug
|
|
get
|
|
{
|
|
String maskPattern = System.Convert.ToString(MaskPatternReferer, 2);
|
|
int length = maskPattern.Length;
|
|
for (int i = 0; i < 3 - length; i++)
|
|
maskPattern = "0" + maskPattern;
|
|
return maskPattern;
|
|
}
|
|
|
|
}
|
|
virtual public int Width
|
|
{
|
|
get
|
|
{
|
|
return width;
|
|
}
|
|
|
|
}
|
|
virtual public int Height
|
|
{
|
|
get
|
|
{
|
|
return height;
|
|
}
|
|
|
|
}
|
|
virtual public int[] Blocks
|
|
{
|
|
get
|
|
{
|
|
int width = Width;
|
|
int height = Height;
|
|
int x = width - 1;
|
|
int y = height - 1;
|
|
System.Collections.ArrayList codeBits = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
|
|
System.Collections.ArrayList codeWords = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
|
|
int tempWord = 0;
|
|
int figure = 7;
|
|
int isNearFinish = 0;
|
|
bool READ_UP = true;
|
|
bool READ_DOWN = false;
|
|
bool direction = READ_UP;
|
|
do
|
|
{
|
|
codeBits.Add(getElement(x, y));
|
|
if (getElement(x, y) == true)
|
|
{
|
|
tempWord += (1 << figure);
|
|
}
|
|
figure--;
|
|
if (figure == - 1)
|
|
{
|
|
codeWords.Add((System.Int32) tempWord);
|
|
figure = 7;
|
|
tempWord = 0;
|
|
}
|
|
// determine module that read next
|
|
do
|
|
{
|
|
if (direction == READ_UP)
|
|
{
|
|
if ((x + isNearFinish) % 2 == 0)
|
|
//if right side of two column
|
|
x--;
|
|
// to left
|
|
else
|
|
{
|
|
if (y > 0)
|
|
{
|
|
//be able to move upper side
|
|
x++;
|
|
y--;
|
|
}
|
|
else
|
|
{
|
|
//can't move upper side
|
|
x--; //change direction
|
|
if (x == 6)
|
|
{
|
|
x--;
|
|
isNearFinish = 1; // after through horizontal Timing Pattern, move pattern is changed
|
|
}
|
|
direction = READ_DOWN;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((x + isNearFinish) % 2 == 0)
|
|
//if left side of two column
|
|
x--;
|
|
else
|
|
{
|
|
if (y < height - 1)
|
|
{
|
|
x++;
|
|
y++;
|
|
}
|
|
else
|
|
{
|
|
x--;
|
|
if (x == 6)
|
|
{
|
|
x--;
|
|
isNearFinish = 1;
|
|
}
|
|
direction = READ_UP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (isInFunctionPattern(x, y));
|
|
}
|
|
while (x != - 1);
|
|
|
|
int[] gotWords = new int[codeWords.Count];
|
|
for (int i = 0; i < codeWords.Count; i++)
|
|
{
|
|
System.Int32 temp = (System.Int32) codeWords[i];
|
|
gotWords[i] = temp;
|
|
}
|
|
return gotWords;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
public virtual bool getElement(int x, int y)
|
|
{
|
|
return moduleMatrix[x][y];
|
|
}
|
|
|
|
public QRCodeSymbol(bool[][] moduleMatrix)
|
|
{
|
|
this.moduleMatrix = moduleMatrix;
|
|
width = moduleMatrix.Length;
|
|
height = moduleMatrix[0].Length;
|
|
initialize();
|
|
}
|
|
|
|
|
|
|
|
internal virtual void initialize()
|
|
{
|
|
//calculate version by number of side modules
|
|
version = (width - 17) / 4;
|
|
Point[][] alignmentPattern = new Point[1][];
|
|
for (int i = 0; i < 1; i++)
|
|
{
|
|
alignmentPattern[i] = new Point[1];
|
|
}
|
|
|
|
int[] logicalSeeds = new int[1];
|
|
if (version >= 2 && version <= 40)
|
|
{
|
|
logicalSeeds = LogicalSeed.getSeed(version);
|
|
Point[][] tmpArray = new Point[logicalSeeds.Length][];
|
|
for (int i2 = 0; i2 < logicalSeeds.Length; i2++)
|
|
{
|
|
tmpArray[i2] = new Point[logicalSeeds.Length];
|
|
}
|
|
alignmentPattern = tmpArray;
|
|
}
|
|
|
|
//obtain alignment pattern's center coodintates by logical seeds
|
|
for (int col = 0; col < logicalSeeds.Length; col++)
|
|
{
|
|
for (int row = 0; row < logicalSeeds.Length; row++)
|
|
{
|
|
alignmentPattern[row][col] = new Point(logicalSeeds[row], logicalSeeds[col]);
|
|
}
|
|
}
|
|
this.alignmentPattern = alignmentPattern;
|
|
dataCapacity = calcDataCapacity();
|
|
bool[] formatInformation = readFormatInformation();
|
|
decodeFormatInformation(formatInformation);
|
|
unmask();
|
|
}
|
|
|
|
internal virtual bool[] readFormatInformation()
|
|
{
|
|
bool[] modules = new bool[15];
|
|
|
|
//obtain format information from symbol
|
|
for (int i = 0; i <= 5; i++)
|
|
modules[i] = getElement(8, i);
|
|
|
|
modules[6] = getElement(8, 7);
|
|
modules[7] = getElement(8, 8);
|
|
modules[8] = getElement(7, 8);
|
|
|
|
for (int i = 9; i <= 14; i++)
|
|
modules[i] = getElement(14 - i, 8);
|
|
|
|
//unmask Format Information's with given mask pattern. (JIS-X-0510(2004), p65)
|
|
int maskPattern = 0x5412;
|
|
|
|
for (int i = 0; i <= 14; i++)
|
|
{
|
|
bool xorBit = false;
|
|
if (((SystemUtils.URShift(maskPattern, i)) & 1) == 1)
|
|
xorBit = true;
|
|
else
|
|
xorBit = false;
|
|
|
|
// get unmasked format information with bit shift
|
|
if (modules[i] == xorBit)
|
|
modules[i] = false;
|
|
else
|
|
modules[i] = true;
|
|
}
|
|
|
|
BCH15_5 corrector = new BCH15_5(modules);
|
|
bool[] output = corrector.correct();
|
|
bool[] formatInformation = new bool[5];
|
|
for (int i = 0; i < 5; i++)
|
|
formatInformation[i] = output[10 + i];
|
|
|
|
return formatInformation;
|
|
}
|
|
|
|
internal virtual void unmask()
|
|
{
|
|
bool[][] maskPattern = generateMaskPattern();
|
|
|
|
int size = Width;
|
|
|
|
for (int y = 0; y < size; y++)
|
|
{
|
|
for (int x = 0; x < size; x++)
|
|
{
|
|
if (maskPattern[x][y] == true)
|
|
{
|
|
reverseElement(x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
internal virtual bool[][] generateMaskPattern()
|
|
{
|
|
int maskPatternReferer = MaskPatternReferer;
|
|
|
|
int width = Width;
|
|
int height = Height;
|
|
bool[][] maskPattern = new bool[width][];
|
|
for (int i = 0; i < width; i++)
|
|
{
|
|
maskPattern[i] = new bool[height];
|
|
}
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
if (isInFunctionPattern(x, y))
|
|
continue;
|
|
switch (maskPatternReferer)
|
|
{
|
|
|
|
case 0: // 000
|
|
if ((y + x) % 2 == 0)
|
|
maskPattern[x][y] = true;
|
|
break;
|
|
|
|
case 1: // 001
|
|
if (y % 2 == 0)
|
|
maskPattern[x][y] = true;
|
|
break;
|
|
|
|
case 2: // 010
|
|
if (x % 3 == 0)
|
|
maskPattern[x][y] = true;
|
|
break;
|
|
|
|
case 3: // 011
|
|
if ((y + x) % 3 == 0)
|
|
maskPattern[x][y] = true;
|
|
break;
|
|
|
|
case 4: // 100
|
|
if ((y / 2 + x / 3) % 2 == 0)
|
|
maskPattern[x][y] = true;
|
|
break;
|
|
|
|
case 5: // 101
|
|
if ((y * x) % 2 + (y * x) % 3 == 0)
|
|
maskPattern[x][y] = true;
|
|
break;
|
|
|
|
case 6: // 110
|
|
if (((y * x) % 2 + (y * x) % 3) % 2 == 0)
|
|
maskPattern[x][y] = true;
|
|
break;
|
|
|
|
case 7: // 111
|
|
if (((y * x) % 3 + (y + x) % 2) % 2 == 0)
|
|
maskPattern[x][y] = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return maskPattern;
|
|
}
|
|
|
|
private int calcDataCapacity()
|
|
{
|
|
int numFunctionPatternModule = 0;
|
|
int numFormatAndVersionInfoModule = 0;
|
|
int version = this.Version;
|
|
//Console.out.println("Version:" + String.valueOf(version));
|
|
|
|
if (version <= 6)
|
|
numFormatAndVersionInfoModule = 31;
|
|
else
|
|
numFormatAndVersionInfoModule = 67;
|
|
|
|
|
|
// the number of finter patterns :
|
|
int sqrtCenters = (version / 7) + 2;
|
|
// the number of modules left when we remove the patterns modules
|
|
// 3*64 for the 3 big ones,
|
|
// sqrtCenters*sqrtCenters)-3)*25 for the small ones
|
|
int modulesLeft = (version == 1?192:192 + ((sqrtCenters * sqrtCenters) - 3) * 25);
|
|
// Don't ask me how I found that one...
|
|
//
|
|
numFunctionPatternModule = modulesLeft + 8 * version + 2 - (sqrtCenters - 2) * 10;
|
|
int dataCapacity = (width * width - numFunctionPatternModule - numFormatAndVersionInfoModule) / 8;
|
|
return dataCapacity;
|
|
}
|
|
|
|
internal virtual void decodeFormatInformation(bool[] formatInformation)
|
|
{
|
|
if (formatInformation[4] == false)
|
|
if (formatInformation[3] == true)
|
|
errorCollectionLevel = 0;
|
|
else
|
|
errorCollectionLevel = 1;
|
|
else if (formatInformation[3] == true)
|
|
errorCollectionLevel = 2;
|
|
else
|
|
errorCollectionLevel = 3;
|
|
|
|
for (int i = 2; i >= 0; i--)
|
|
if (formatInformation[i] == true)
|
|
maskPattern += (1 << i);
|
|
}
|
|
|
|
public virtual void reverseElement(int x, int y)
|
|
{
|
|
moduleMatrix[x][y] = !moduleMatrix[x][y];
|
|
}
|
|
|
|
public virtual bool isInFunctionPattern(int targetX, int targetY)
|
|
{
|
|
if (targetX < 9 && targetY < 9)
|
|
//in Left-Up Finder Pattern or function patterns around it
|
|
return true;
|
|
if (targetX > Width - 9 && targetY < 9)
|
|
//in Right-up Finder Pattern or function patterns around it
|
|
return true;
|
|
if (targetX < 9 && targetY > Height - 9)
|
|
//in Left-bottom Finder Pattern or function patterns around it
|
|
return true;
|
|
|
|
if (version >= 7)
|
|
{
|
|
if (targetX > Width - 12 && targetY < 6)
|
|
return true;
|
|
if (targetX < 6 && targetY > Height - 12)
|
|
return true;
|
|
}
|
|
// in timing pattern
|
|
if (targetX == 6 || targetY == 6)
|
|
return true;
|
|
|
|
// in alignment pattern.
|
|
Point[][] alignmentPattern = AlignmentPattern;
|
|
int sideLength = alignmentPattern.Length;
|
|
|
|
for (int y = 0; y < sideLength; y++)
|
|
{
|
|
for (int x = 0; x < sideLength; x++)
|
|
{
|
|
if (!(x == 0 && y == 0) && !(x == sideLength - 1 && y == 0) && !(x == 0 && y == sideLength - 1))
|
|
if (System.Math.Abs(alignmentPattern[x][y].X - targetX) < 3 && System.Math.Abs(alignmentPattern[x][y].Y - targetY) < 3)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
} |