Click here to Skip to main content
15,897,891 members
Articles / Programming Languages / C#

Open Source QRCode Library

Rate me:
Please Sign up or sign in to vote.
4.92/5 (147 votes)
20 Sep 2007CPOL1 min read 6M   192.3K   421  
How to use QRCode library to encode and decode QRCode
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;
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Malaysia Malaysia
A programmer for a long time, and still learning everyday.

A supporter for open source solutions, and have written quite a few open source software both in .NET and Java.

https://mengwangk.github.io/

Comments and Discussions