public getNextUnset ( int from ) : int | ||
from | int | index to start looking for unset bit |
return | int |
override public Result decodeRow(int rowNumber, BitArray row, IDictionary<DecodeHintType, object> hints) { int[] startPatternInfo = findStartPattern(row); if (startPatternInfo == null) return null; int startCode = startPatternInfo[2]; int codeSet; switch (startCode) { case CODE_START_A: codeSet = CODE_CODE_A; break; case CODE_START_B: codeSet = CODE_CODE_B; break; case CODE_START_C: codeSet = CODE_CODE_C; break; default: return null; } bool done = false; bool isNextShifted = false; var result = new StringBuilder(20); var rawCodes = new List<byte>(20); int lastStart = startPatternInfo[0]; int nextStart = startPatternInfo[1]; int[] counters = new int[6]; int lastCode = 0; int code = 0; int checksumTotal = startCode; int multiplier = 0; bool lastCharacterWasPrintable = true; while (!done) { bool unshift = isNextShifted; isNextShifted = false; // Save off last code lastCode = code; // Decode another code from image if (!decodeCode(row, counters, nextStart, out code)) return null; rawCodes.Add((byte)code); // Remember whether the last code was printable or not (excluding CODE_STOP) if (code != CODE_STOP) { lastCharacterWasPrintable = true; } // Add to checksum computation (if not CODE_STOP of course) if (code != CODE_STOP) { multiplier++; checksumTotal += multiplier * code; } // Advance to where the next code will to start lastStart = nextStart; foreach (int counter in counters) { nextStart += counter; } // Take care of illegal start codes switch (code) { case CODE_START_A: case CODE_START_B: case CODE_START_C: return null; } switch (codeSet) { case CODE_CODE_A: if (code < 64) { result.Append((char)(' ' + code)); } else if (code < 96) { result.Append((char)(code - 64)); } else { // Don't let CODE_STOP, which always appears, affect whether whether we think the last // code was printable or not. if (code != CODE_STOP) { lastCharacterWasPrintable = false; } switch (code) { case CODE_FNC_1: if (result.Length == 0) { // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code // is FNC1 then this is GS1-128. We add the symbology identifier. result.Append("]C1"); } else { // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS) result.Append((char)29); } break; case CODE_FNC_2: case CODE_FNC_3: case CODE_FNC_4_A: // do nothing? break; case CODE_SHIFT: isNextShifted = true; codeSet = CODE_CODE_B; break; case CODE_CODE_B: codeSet = CODE_CODE_B; break; case CODE_CODE_C: codeSet = CODE_CODE_C; break; case CODE_STOP: done = true; break; } } break; case CODE_CODE_B: if (code < 96) { result.Append((char)(' ' + code)); } else { if (code != CODE_STOP) { lastCharacterWasPrintable = false; } switch (code) { case CODE_FNC_1: case CODE_FNC_2: case CODE_FNC_3: case CODE_FNC_4_B: // do nothing? break; case CODE_SHIFT: isNextShifted = true; codeSet = CODE_CODE_A; break; case CODE_CODE_A: codeSet = CODE_CODE_A; break; case CODE_CODE_C: codeSet = CODE_CODE_C; break; case CODE_STOP: done = true; break; } } break; case CODE_CODE_C: if (code < 100) { if (code < 10) { result.Append('0'); } result.Append(code); } else { if (code != CODE_STOP) { lastCharacterWasPrintable = false; } switch (code) { case CODE_FNC_1: // do nothing? break; case CODE_CODE_A: codeSet = CODE_CODE_A; break; case CODE_CODE_B: codeSet = CODE_CODE_B; break; case CODE_STOP: done = true; break; } } break; } // Unshift back to another code set if we were shifted if (unshift) { codeSet = codeSet == CODE_CODE_A ? CODE_CODE_B : CODE_CODE_A; } } // Check for ample whitespace following pattern, but, to do this we first need to remember that // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left // to read off. Would be slightly better to properly read. Here we just skip it: nextStart = row.getNextUnset(nextStart); if (!row.isRange(nextStart, Math.Min(row.Size, nextStart + (nextStart - lastStart) / 2), false)) { return null; } // Pull out from sum the value of the penultimate check code checksumTotal -= multiplier * lastCode; // lastCode is the checksum then: if (checksumTotal % 103 != lastCode) { return null; } // Need to pull out the check digits from string int resultLength = result.Length; if (resultLength == 0) { // false positive return null; } // Only bother if the result had at least one character, and if the checksum digit happened to // be a printable character. If it was just interpreted as a control code, nothing to remove. if (resultLength > 0 && lastCharacterWasPrintable) { if (codeSet == CODE_CODE_C) { result.Remove(resultLength - 2, 2); } else { result.Remove(resultLength - 1, 1); } } float left = (startPatternInfo[1] + startPatternInfo[0]) / 2.0f; float right = (nextStart + lastStart) / 2.0f; var resultPointCallback = hints == null || !hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK) ? null : (ResultPointCallback)hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK]; if (resultPointCallback != null) { resultPointCallback(new ResultPoint(left, rowNumber)); resultPointCallback(new ResultPoint(right, rowNumber)); } int rawCodesSize = rawCodes.Count; var rawBytes = new byte[rawCodesSize]; for (int i = 0; i < rawCodesSize; i++) { rawBytes[i] = rawCodes[i]; } return new Result( result.ToString(), rawBytes, new [] { new ResultPoint(left, rowNumber), new ResultPoint(right, rowNumber) }, BarcodeFormat.CODE_128); }
/// <summary> /// </summary> /// <param name="row">row of black/white values to search</param> /// <param name="rowOffset">position to start search</param> /// <param name="whiteFirst">if true, indicates that the pattern specifies white/black/white/...</param> /// pixel counts, otherwise, it is interpreted as black/white/black/... /// <param name="pattern">pattern of counts of number of black and white pixels that are being</param> /// searched for as a pattern /// <param name="counters">array of counters, as long as pattern, to re-use</param> /// <returns>start/end horizontal offset of guard pattern, as an array of two ints</returns> internal static int[] findGuardPattern(BitArray row, int rowOffset, bool whiteFirst, int[] pattern, int[] counters) { int patternLength = pattern.Length; int width = row.Size; bool isWhite = whiteFirst; rowOffset = whiteFirst ? row.getNextUnset(rowOffset) : row.getNextSet(rowOffset); int counterPosition = 0; int patternStart = rowOffset; for (int x = rowOffset; x < width; x++) { if (row[x] ^ isWhite) { counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { return new int[] { patternStart, x }; } patternStart += counters[0] + counters[1]; Array.Copy(counters, 2, counters, 0, patternLength - 2); counters[patternLength - 2] = 0; counters[patternLength - 1] = 0; counterPosition--; } else { counterPosition++; } counters[counterPosition] = 1; isWhite = !isWhite; } } return null; }
/// <summary> /// Records the size of all runs of white and black pixels, starting with white. /// This is just like recordPattern, except it records all the counters, and /// uses our builtin "counters" member for storage. /// </summary> /// <param name="row">row to count from</param> private bool setCounters(BitArray row) { counterLength = 0; // Start from the first white bit. int i = row.getNextUnset(0); int end = row.Size; if (i >= end) { return false; } bool isWhite = true; int count = 0; while (i < end) { if (row[i] ^ isWhite) { // that is, exactly one is true count++; } else { counterAppend(count); count = 1; isWhite = !isWhite; } i++; } counterAppend(count); return true; }
private FinderPattern parseFoundFinderPattern(BitArray row, int rowNumber, bool oddPattern) { // Actually we found elements 2-5. int firstCounter; int start; int end; if (oddPattern) { // If pattern number is odd, we need to locate element 1 *before* the current block. int firstElementStart = startEnd[0] - 1; // Locate element 1 while (firstElementStart >= 0 && !row[firstElementStart]) { firstElementStart--; } firstElementStart++; firstCounter = startEnd[0] - firstElementStart; start = firstElementStart; end = startEnd[1]; } else { // If pattern number is even, the pattern is reversed, so we need to locate element 1 *after* the current block. start = startEnd[0]; end = row.getNextUnset(startEnd[1] + 1); firstCounter = end - startEnd[1]; } // Make 'counters' hold 1-4 int[] counters = getDecodeFinderCounters(); Array.Copy(counters, 0, counters, 1, counters.Length - 1); counters[0] = firstCounter; int value; if (!parseFinderValue(counters, FINDER_PATTERNS, out value)) return null; return new FinderPattern(value, new int[] { start, end }, start, end, rowNumber); }
private static int getNextSecondBar(BitArray row, int initialPos) { int currentPos; if (row[initialPos]) { currentPos = row.getNextUnset(initialPos); currentPos = row.getNextSet(currentPos); } else { currentPos = row.getNextSet(initialPos); currentPos = row.getNextUnset(currentPos); } return currentPos; }
int decodeMiddle(BitArray row, int[] startRange, StringBuilder resultString) { int[] counters = decodeMiddleCounters; counters[0] = 0; counters[1] = 0; counters[2] = 0; counters[3] = 0; int end = row.Size; int rowOffset = startRange[1]; int checkParity = 0; for (int x = 0; x < 2 && rowOffset < end; x++) { int bestMatch; if (!UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS, out bestMatch)) return -1; resultString.Append((char)('0' + bestMatch % 10)); foreach (int counter in counters) { rowOffset += counter; } if (bestMatch >= 10) { checkParity |= 1 << (1 - x); } if (x != 1) { // Read off separator if not last rowOffset = row.getNextSet(rowOffset); rowOffset = row.getNextUnset(rowOffset); } } if (resultString.Length != 2) { return -1; } if (int.Parse(resultString.ToString()) % 4 != checkParity) { return -1; } return rowOffset; }
int decodeMiddle(BitArray row, int[] startRange, StringBuilder resultString) { int[] counters = decodeMiddleCounters; counters[0] = 0; counters[1] = 0; counters[2] = 0; counters[3] = 0; int end = row.Size; int rowOffset = startRange[1]; int lgPatternFound = 0; for (int x = 0; x < 5 && rowOffset < end; x++) { int bestMatch; if (!UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS, out bestMatch)) return -1; resultString.Append((char)('0' + bestMatch % 10)); foreach (int counter in counters) { rowOffset += counter; } if (bestMatch >= 10) { lgPatternFound |= 1 << (4 - x); } if (x != 4) { // Read off separator if not last rowOffset = row.getNextSet(rowOffset); rowOffset = row.getNextUnset(rowOffset); } } if (resultString.Length != 5) { return -1; } int checkDigit; if (!determineCheckDigit(lgPatternFound, out checkDigit)) return -1; if (extensionChecksum(resultString.ToString()) != checkDigit) { return -1; } return rowOffset; }