internal FinderPatternInfo Find(Hashtable hints) { bool tryHarder = hints?.ContainsKey(DecodeHintType.TRY_HARDER) == true; int maxI = Image.Height; int maxJ = Image.Width; // We are looking for black/white/black/white/black modules in // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the // image, and then account for the center being 3 modules in size. This gives the smallest // number of pixels the center could be, so skip this often. When trying harder, look for all // QR versions regardless of how dense they are. int iSkip = (3 * maxI) / (4 * MAX_MODULES); if (iSkip < MIN_SKIP || tryHarder) { iSkip = MIN_SKIP; } bool done = false; int[] stateCount = new int[5]; for (int i = iSkip - 1; i < maxI && !done; i += iSkip) { // Get a row of black/white values stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; int currentState = 0; for (int j = 0; j < maxJ; j++) { if (Image.Get_Renamed(j, i)) { // Black pixel if ((currentState & 1) == 1) { // Counting white pixels currentState++; } stateCount[currentState]++; } else { // White pixel if ((currentState & 1) == 0) { // Counting black pixels if (currentState == 4) { // A winner? if (FoundPatternCross(stateCount)) { // Yes bool confirmed = HandlePossibleCenter(stateCount, i, j); if (confirmed) { // Start examining every other line. Checking each line turned out to be too // expensive and didn't improve performance. iSkip = 2; if (hasSkipped) { done = HaveMultiplyConfirmedCenters(); } else { int rowSkip = FindRowSkip(); if (rowSkip > stateCount[2]) { // Skip rows between row of lower confirmed center // and top of presumed third confirmed center // but back up a bit to get a full chance of detecting // it, entire width of center of finder pattern // Skip by rowSkip, but back off by stateCount[2] (size of last center // of pattern we saw) to be conservative, and also back off by iSkip which // is about to be re-added i += rowSkip - stateCount[2] - iSkip; j = maxJ - 1; } } } else { // Advance to next black pixel do { j++; }while (j < maxJ && !Image.Get_Renamed(j, i)); j--; // back up to that last white pixel } // Clear state to start looking again currentState = 0; stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; } else { // No, shift counts back by two stateCount[0] = stateCount[2]; stateCount[1] = stateCount[3]; stateCount[2] = stateCount[4]; stateCount[3] = 1; stateCount[4] = 0; currentState = 3; } } else { stateCount[++currentState]++; } } else { // Counting white pixels stateCount[currentState]++; } } } if (FoundPatternCross(stateCount)) { bool confirmed = HandlePossibleCenter(stateCount, i, maxJ); if (confirmed) { iSkip = stateCount[0]; if (hasSkipped) { // Found a third one done = HaveMultiplyConfirmedCenters(); } } } } FinderPattern[] patternInfo = SelectBestPatterns(); ResultPoint.OrderBestPatterns(patternInfo); return(new FinderPatternInfo(patternInfo)); }