/// <summary> /// Tries to decode a QR Code in a given bitmap. /// </summary> /// <param name="bm">The bitmap to be parsed.</param> /// <returns>A string with the contents of the barcode if successful, null otherwise</returns> public static unsafe string TryDecode(Bitmap bm, bool inverted) { _width = bm.Width; _height = bm.Height; BitmapData bmData = bm.LockBits(new Rectangle(0, 0, _width, _height), ImageLockMode.ReadOnly, bm.PixelFormat); int imageBpp = 0; switch (bm.PixelFormat) { case PixelFormat.Format32bppArgb: imageBpp = 4; break; case PixelFormat.Format24bppRgb: imageBpp = 3; break; case PixelFormat.Format8bppIndexed: imageBpp = 1; break; default: break; } byte* ptr = (byte*)bmData.Scan0.ToPointer(); int padding = bmData.Stride - _width * imageBpp; int[] imgArray = new int[_width * _height]; for (int y = 0; y < _height; y++) { for (int x = 0; x < (_width * imageBpp); x += imageBpp) { if (!inverted) { if (ptr[0] < 127) { imgArray[y * _width + x / imageBpp] = 1; } else if (ptr[0] > 127) imgArray[y * _width + x / imageBpp] = 0; } else { if (ptr[0] < 127) imgArray[y * _width + x / imageBpp] = 0; else if (ptr[0] > 127) imgArray[y * _width + x / imageBpp] = 1; } ptr += imageBpp; } ptr += padding; } bm.UnlockBits(bmData); _imageArray = imgArray; FixedSizeQueue<int> stateCount = new FixedSizeQueue<int>(5); FixedSizeQueue<int> pixelColor = new FixedSizeQueue<int>(5); int[] correctFinderPatternArray = new int[5] { 1, 0, 1, 0, 1 }; List<FinderPattern> finderPatterns = new List<FinderPattern>(); int currentColorCount = 0; bool correctPixelColor = true; bool foundStartPixelColor = false; bool doneSearching = false; int currentState = 0; for (int y = 0; y < _height; y++) { for (int x = 0; x < _width; x++) { int currentPixel = imgArray[y * _width + x]; if (currentPixel == 1) // Current pixel is black { currentColorCount++; if (x == _width - 1 && y == _height - 1) { doneSearching = true; } if (!doneSearching && imgArray[y * _width + x + 1] == 0) { // Next pixel in line is white stateCount.Enqueue(currentColorCount); if (!foundStartPixelColor) { pixelColor.Enqueue(1); int[] array = pixelColor.GetArray(); if (correctFinderPatternArray.SequenceEqual(array)) foundStartPixelColor = true; } else correctPixelColor = !correctPixelColor; currentColorCount = 0; currentState++; if (currentState >= 5) { int[] stateCountArray = stateCount.GetArray(); // We found a black/white/black/white/black pattern, now check its ratios //if (x > 2009 && y > 880) System.Diagnostics.Debugger.Break(); if (validFinderPattern(stateCountArray) && correctPixelColor) { float patternMiddle = verticalFinderPatternCheck(stateCountArray, x, y); if (!float.IsNaN(patternMiddle)) { finderPatterns.Add(new FinderPattern(stateCountArray, (int)Math.Round(stateCountCenter(stateCountArray, x)), (int)Math.Round(patternMiddle))); } } } } } else if (currentPixel == 0) // Current pixel is white { currentColorCount++; if (x == _width - 1 && y == _height - 1) { doneSearching = true; } if (!doneSearching && imgArray[y * _width + x + 1] == 1) { stateCount.Enqueue(currentColorCount); if (!foundStartPixelColor) { pixelColor.Enqueue(0); int[] array = pixelColor.GetArray(); if (correctFinderPatternArray.SequenceEqual(array)) foundStartPixelColor = true; } else correctPixelColor = !correctPixelColor; currentColorCount = 0; currentState++; // Next pixel in line is black } } } } if (doneSearching) { List<FinderPattern> uniqueFinderPatterns = new List<FinderPattern>(); foreach (FinderPattern pattern in finderPatterns) { int currentX = pattern.X; int currentY = pattern.Y; bool listContainsFinderPattern = false; foreach (FinderPattern uniquePattern in uniqueFinderPatterns) { if (Math.Abs(currentX - uniquePattern.X) < (0.05f * currentX) && Math.Abs(currentY - uniquePattern.Y) < (0.05f * currentY)) listContainsFinderPattern = true; } if (!listContainsFinderPattern) uniqueFinderPatterns.Add(pattern); } if (uniqueFinderPatterns.Count > 3) { TryDecode(ImageProcessing.RotateImageByAngle(bm, 45.0f), false); } if (uniqueFinderPatterns.Count < 3 && !inverted) // We do not have three finder patterns, repeat the process with an inverted image, as per the spec { TryDecode(bm, true); } else // We have the necessary number of finder patterns, proceed with decoding { float codeAngle; for (int i = 0; i < uniqueFinderPatterns.Count; i++) { } //uniqueFinderPatterns[2] = uniqueFinderPatterns[3]; bool result = sortUniqueFinderPatterns(ref uniqueFinderPatterns, out codeAngle); if (result) { middleOfFinderPatterns(ref uniqueFinderPatterns); updateStateCounts(ref uniqueFinderPatterns); float dx = (float)Math.Cos(codeAngle); float dy = (float)Math.Sin(codeAngle); float d = (float)Math.Sqrt(Math.Pow(uniqueFinderPatterns[1].X - uniqueFinderPatterns[0].X, 2) + Math.Pow(uniqueFinderPatterns[1].Y - uniqueFinderPatterns[0].Y, 2)); int[] upperLeftStateCount = uniqueFinderPatterns[0].StateCount; int[] upperRightStateCount = uniqueFinderPatterns[1].StateCount; int widthUpperLeft = upperLeftStateCount[0] + upperLeftStateCount[1] + upperLeftStateCount[2] + upperLeftStateCount[3] + upperLeftStateCount[4]; int widthUpperRight = upperRightStateCount[0] + upperRightStateCount[1] + upperRightStateCount[2] + upperRightStateCount[3] + upperRightStateCount[4]; float nominalXDimension = (widthUpperLeft + widthUpperRight) / 14.0f; int symbolVersion = (int)Math.Round(((d / nominalXDimension) - 10) / 4.0f); float alpha = (codeAngle / 180.0f) * (float)Math.PI; if (symbolVersion >= 7) { float upperRightModuleSize = widthUpperRight / 7.0f; string versionInformation1 = ""; float versionInformation1X = uniqueFinderPatterns[1].X - (7.0f * nominalXDimension); float versionInformation1Y = uniqueFinderPatterns[1].Y - (3.0f * nominalXDimension); for (int versionY = -3; versionY < 3; versionY++) { for (int versionX = -7; versionX < -4; versionX++) { float beta = (float)Math.Atan2(versionY, versionX); float distance = versionX * upperRightModuleSize / (float)Math.Cos(beta); int xC = (int)Math.Round(distance * Math.Cos(alpha + beta) + uniqueFinderPatterns[1].X); int yC = (int)Math.Round(distance * Math.Sin(alpha + beta) + uniqueFinderPatterns[1].Y); if (_imageArray[yC * _width + xC] == 1) versionInformation1 += "1"; else versionInformation1 += "0"; } } int? readVersion = qrCodeVersion(versionInformation1); if (readVersion != null) symbolVersion = (int)readVersion; else return ""; } int numberOfModules = 17 + symbolVersion * 4; int xDifference = Math.Abs(uniqueFinderPatterns[1].X - uniqueFinderPatterns[0].X); int yDifference = Math.Abs(uniqueFinderPatterns[1].Y - uniqueFinderPatterns[0].Y); float finderPatternDistance = (float)Math.Sqrt(xDifference * xDifference + yDifference * yDifference); float moduleSize = finderPatternDistance / (numberOfModules - 7); bool[,] qrCode = new bool[numberOfModules,numberOfModules]; for (int symbolY = -3; symbolY < numberOfModules - 3; symbolY++) { string str = ""; for (int symbolX = -3; symbolX < numberOfModules - 3; symbolX++) { float distance; float beta = (float)Math.Atan2(symbolY, symbolX); if (beta < 0) beta += 2.0f * (float)Math.PI; if ((beta > 0.25f * Math.PI && beta < 0.75f * Math.PI) || (beta > 1.25f * Math.PI && beta < 1.75f * Math.PI)) { distance = symbolY * moduleSize / (float)Math.Sin(beta); } else { distance = symbolX * moduleSize / (float)Math.Cos(beta); } int xC = (int)Math.Round(distance * Math.Cos(alpha + beta) + uniqueFinderPatterns[0].X); int yC = (int)Math.Round(distance * Math.Sin(alpha + beta) + uniqueFinderPatterns[0].Y); if (xC < 0) xC = 0; if (yC < 0) yC = 0; if (_imageArray[yC * _width + xC] == 1) { qrCode[symbolX + 3, symbolY + 3] = true; str += "1"; } else { qrCode[symbolX + 3, symbolY + 3] = false; str += "0"; } } } System.Diagnostics.Debug.WriteLine(qrCode[4, 4]); int ecLevel; // M=0; L=1; H=2; Q=3 int dataMaskingPattern; formatInformation(qrCode, numberOfModules, out ecLevel, out dataMaskingPattern); releaseDataMasking(ref qrCode, numberOfModules, dataMaskingPattern); byte[] rawData = performDecode(qrCode, numberOfModules); int ordinalECLevel = getOrdinalECLevel(ecLevel); QRCodeVersion currentVersion = versions[arrayVersionNumber(numberOfModules)]; ECBlocks currentECBlocks = currentVersion.ECBlock(ordinalECLevel); ECB[] ecBlockArray = currentECBlocks.getECBlocks(); int blockNumber = currentECBlocks.NumBlocks; int[] dataCodewordsPerBlock = new int[blockNumber]; for (int i = 0; i < ecBlockArray[0].Count; i++) { dataCodewordsPerBlock[i] = ecBlockArray[0].DataCodewords; } if (ecBlockArray.Length == 2) { for (int i = 0; i < ecBlockArray[1].Count; i++) { dataCodewordsPerBlock[i] = ecBlockArray[1].DataCodewords; } } int ecCodewordsPerBlock = currentECBlocks.ECCodewordsPerBlock; int errorCorrectionBoundary = 0; byte[][] orderedDataCodewords = new byte[blockNumber][]; byte[][] orderedECCodewords = new byte[blockNumber][]; int runCounter = 0; for (int i = 0; i < ecBlockArray.Length; i++) { ECB currentECBlock = ecBlockArray[i]; int count = currentECBlock.Count; int numberOfDataCodewords = currentECBlock.DataCodewords; for (int j = 0; j < count; j++) { byte[] currentBlockDataCodewords = new byte[numberOfDataCodewords]; for (int k = 0; k < numberOfDataCodewords; k++) { int rawDataBytePosition = j + k * count; currentBlockDataCodewords[k] = rawData[rawDataBytePosition]; errorCorrectionBoundary++; } orderedDataCodewords[runCounter++] = currentBlockDataCodewords; } } for (int i = 0; i < blockNumber; i++) { byte[] currentBlockECCodewords = new byte[ecCodewordsPerBlock]; for (int j = 0; j < ecCodewordsPerBlock; j++) { int rawDataBytePosition = errorCorrectionBoundary + i + j * blockNumber; currentBlockECCodewords[j] = rawData[rawDataBytePosition]; } orderedECCodewords[i] = currentBlockECCodewords; } byte[][] orderedCodewords = new byte[blockNumber][]; for (int i = 0; i < orderedDataCodewords.GetLength(0); i++) { byte[] temp = new byte[orderedDataCodewords[i].Length + orderedECCodewords[i].Length]; orderedDataCodewords[i].CopyTo(temp, 0); orderedECCodewords[i].CopyTo(temp, orderedDataCodewords[i].Length); orderedCodewords[i] = temp; } int p = currentVersion.getNumberOfMisdecodeProtectionCodewords(ordinalECLevel); int numberOfSyndromes = ecCodewordsPerBlock - p; int currentBlock = 0; List<int> finalDataCodewords = new List<int>(); foreach (byte[] block in orderedCodewords) { int[] intBlock = byteArrayToIntArray(block); int[] corrected = ReedSolomon.CorrectMessage(intBlock, numberOfSyndromes); if (!corrected.SequenceEqual<int>(new int[] { -1 })) { List<int> list = corrected.ToList<int>(); int dataCodewords = dataCodewordsPerBlock[currentBlock]; finalDataCodewords = finalDataCodewords.Concat(list.GetRange(0, dataCodewords)).ToList(); } else return null; currentBlock++; } System.Diagnostics.Debug.WriteLine(finalDataCodewords); int fLength = finalDataCodewords.Count; int[] shiftedFinalDataCodewords = new int[fLength - 2]; int messageLength = ((finalDataCodewords[0] & 0xf) << 4) ^ ((finalDataCodewords[1] & 0xf0) >> 4); for (int i = 1; i < fLength - 1; i++) { int currentCodeword = finalDataCodewords[i]; int nextCodeword = finalDataCodewords[i + 1]; int temp = (int)((currentCodeword & 0xf) << 4); int temp2 = (int)((nextCodeword & 0xf0) >> 4); int final = temp ^ temp2; shiftedFinalDataCodewords[i - 1] = final; } return System.Text.Encoding.GetEncoding("iso-8859-1").GetString(intArrayToByteArray(shiftedFinalDataCodewords)).Substring(0, messageLength); } } } return ""; }