public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) { int[] start = findAsteriskPattern(row); int nextStart = start[1]; int end = row.Size; // Read off white space while (nextStart < end && !row.get_Renamed(nextStart)) { nextStart++; } System.Text.StringBuilder result = new System.Text.StringBuilder(20); int[] counters = new int[9]; char decodedChar; int lastStart; do { recordPattern(row, nextStart, counters); int pattern = toNarrowWidePattern(counters); if (pattern < 0) { throw ReaderException.Instance; } decodedChar = patternToChar(pattern); result.Append(decodedChar); lastStart = nextStart; for (int i = 0; i < counters.Length; i++) { nextStart += counters[i]; } // Read off white space while (nextStart < end && !row.get_Renamed(nextStart)) { nextStart++; } } while (decodedChar != '*'); result.Remove(result.Length - 1, 1); // remove asterisk // Look for whitespace after pattern: int lastPatternSize = 0; for (int i = 0; i < counters.Length; i++) { lastPatternSize += counters[i]; } int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize; // If 50% of last pattern size, following last pattern, is not whitespace, fail // (but if it's whitespace to the very end of the image, that's OK) if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize) { throw ReaderException.Instance; } if (usingCheckDigit) { int max = result.Length - 1; int total = 0; for (int i = 0; i < max; i++) { total += ALPHABET_STRING.IndexOf((System.Char) result[i]); } if (total % 43 != ALPHABET_STRING.IndexOf((System.Char) result[max])) { throw ReaderException.Instance; } result.Remove(max, 1); } System.String resultString = result.ToString(); if (extendedMode) { resultString = decodeExtended(resultString); } if (resultString.Length == 0) { // Almost surely a false positive throw ReaderException.Instance; } //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" float left = (float) (start[1] + start[0]) / 2.0f; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" float right = (float) (nextStart + lastStart) / 2.0f; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" return new Result(resultString, null, new ResultPoint[]{new ResultPoint(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_39); }
private static int[] findAsteriskPattern(BitArray row) { int width = row.Size; int rowOffset = 0; while (rowOffset < width) { if (row.get_Renamed(rowOffset)) { break; } rowOffset++; } int counterPosition = 0; int[] counters = new int[9]; int patternStart = rowOffset; bool isWhite = false; int patternLength = counters.Length; for (int i = rowOffset; i < width; i++) { bool pixel = row.get_Renamed(i); if (pixel ^ isWhite) { counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { if (toNarrowWidePattern(counters) == ASTERISK_ENCODING) { // Look for whitespace before start pattern, >= 50% of width of start pattern if (row.isRange(System.Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false)) { return new int[]{patternStart, i}; } } patternStart += counters[0] + counters[1]; for (int y = 2; y < patternLength; y++) { counters[y - 2] = counters[y]; } counters[patternLength - 2] = 0; counters[patternLength - 1] = 0; counterPosition--; } else { counterPosition++; } counters[counterPosition] = 1; isWhite = !isWhite; } } throw ReaderException.Instance; }
/// <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/... /// pixel counts, otherwise, it is interpreted as black/white/black/... /// </param> /// <param name="pattern">pattern of counts of number of black and white pixels that are being /// searched for as a pattern /// </param> /// <returns> start/end horizontal offset of guard pattern, as an array of two ints /// </returns> /// <throws> ReaderException if pattern is not found </throws> internal static int[] findGuardPattern(BitArray row, int rowOffset, bool whiteFirst, int[] pattern) { int patternLength = pattern.Length; int[] counters = new int[patternLength]; int width = row.Size; bool isWhite = false; while (rowOffset < width) { isWhite = !row.get_Renamed(rowOffset); if (whiteFirst == isWhite) { break; } rowOffset++; } int counterPosition = 0; int patternStart = rowOffset; for (int x = rowOffset; x < width; x++) { bool pixel = row.get_Renamed(x); if (pixel ^ 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]; for (int y = 2; y < patternLength; y++) { counters[y - 2] = counters[y]; } counters[patternLength - 2] = 0; counters[patternLength - 1] = 0; counterPosition--; } else { counterPosition++; } counters[counterPosition] = 1; isWhite = !isWhite; } } throw ReaderException.Instance; }
/// <summary> The start & end patterns must be pre/post fixed by a quiet zone. This /// zone must be at least 10 times the width of a narrow line. Scan back until /// we either get to the start of the barcode or match the necessary number of /// quiet zone pixels. /// /// Note: Its assumed the row is reversed when using this method to find /// quiet zone after the end pattern. /// /// ref: http://www.barcode-1.net/i25code.html /// /// </summary> /// <param name="row">bit array representing the scanned barcode. /// </param> /// <param name="startPattern">index into row of the start or end pattern. /// </param> /// <throws> ReaderException if the quiet zone cannot be found, a ReaderException is thrown. </throws> private void validateQuietZone(BitArray row, int startPattern) { int quietCount = this.narrowLineWidth * 10; // expect to find this many pixels of quiet zone for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) { if (row.get_Renamed(i)) { break; } quietCount--; } if (quietCount != 0) { // Unable to find the necessary number of quiet zone pixels. throw ReaderException.Instance; } }
/// <summary> Skip all whitespace until we get to the first black line. /// /// </summary> /// <param name="row">row of black/white values to search /// </param> /// <returns> index of the first black line. /// </returns> /// <throws> ReaderException Throws exception if no black lines are found in the row </throws> private static int skipWhiteSpace(BitArray row) { int width = row.Size; int endStart = 0; while (endStart < width) { if (row.get_Renamed(endStart)) { break; } endStart++; } if (endStart == width) { throw ReaderException.Instance; } return endStart; }
/// <param name="row"> row of black/white values to search /// </param> /// <param name="rowOffset">position to start search /// </param> /// <param name="pattern"> pattern of counts of number of black and white pixels that are /// being searched for as a pattern /// </param> /// <returns> start/end horizontal offset of guard pattern, as an array of two /// ints /// </returns> /// <throws> ReaderException if pattern is not found </throws> private static int[] findGuardPattern(BitArray row, int rowOffset, int[] pattern) { // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be // merged to a single method. int patternLength = pattern.Length; int[] counters = new int[patternLength]; int width = row.Size; bool isWhite = false; int counterPosition = 0; int patternStart = rowOffset; for (int x = rowOffset; x < width; x++) { bool pixel = row.get_Renamed(x); if (pixel ^ 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]; for (int y = 2; y < patternLength; y++) { counters[y - 2] = counters[y]; } counters[patternLength - 2] = 0; counters[patternLength - 1] = 0; counterPosition--; } else { counterPosition++; } counters[counterPosition] = 1; isWhite = !isWhite; } } throw ReaderException.Instance; }
/// <summary> Records the size of successive runs of white and black pixels in a row, starting at a given point. /// The values are recorded in the given array, and the number of runs recorded is equal to the size /// of the array. If the row starts on a white pixel at the given start point, then the first count /// recorded is the run of white pixels starting from that point; likewise it is the count of a run /// of black pixels if the row begin on a black pixels at that point. /// /// </summary> /// <param name="row">row to count from /// </param> /// <param name="start">offset into row to start at /// </param> /// <param name="counters">array into which to record counts /// </param> /// <throws> ReaderException if counters cannot be filled entirely from row before running out </throws> /// <summary> of pixels /// </summary> internal static void recordPattern(BitArray row, int start, int[] counters) { int numCounters = counters.Length; for (int i = 0; i < numCounters; i++) { counters[i] = 0; } int end = row.Size; if (start >= end) { throw ReaderException.Instance; } bool isWhite = !row.get_Renamed(start); int counterPosition = 0; int i2 = start; while (i2 < end) { bool pixel = row.get_Renamed(i2); if (pixel ^ isWhite) { // that is, exactly one is true counters[counterPosition]++; } else { counterPosition++; if (counterPosition == numCounters) { break; } else { counters[counterPosition] = 1; isWhite ^= true; // isWhite = !isWhite; } } i2++; } // If we read fully the last section of pixels and filled up our counters -- or filled // the last counter but ran off the side of the image, OK. Otherwise, a problem. if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i2 == end))) { throw ReaderException.Instance; } }