-/*\r
+/*\r
+* Copyright 2008 ZXing authors\r
+*\r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
* You may obtain a copy of the License at\r
* See the License for the specific language governing permissions and\r
* limitations under the License.\r
*/\r
+using System;\r
+using BarcodeFormat = com.google.zxing.BarcodeFormat;\r
+using DecodeHintType = com.google.zxing.DecodeHintType;\r
+using ReaderException = com.google.zxing.ReaderException;\r
+using Result = com.google.zxing.Result;\r
+using ResultPoint = com.google.zxing.ResultPoint;\r
+using BitArray = com.google.zxing.common.BitArray;\r
namespace com.google.zxing.oned\r
{\r
- /**\r
- * <p>Implements decoding of the EAN-13 format.</p>\r
- *\r
- * @author dswitkin@google.com (Daniel Switkin)\r
- * @author Sean Owen\r
- * @author alasdair@google.com (Alasdair Mackintosh)\r
- */\r
-\r
- using System.Text;\r
- using com.google.zxing.common;\r
-\r
- public sealed class ITFReader : AbstractOneDReader\r
- { \r
- private static int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f);\r
- private static int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8f);\r
-\r
- private static int W = 3; // Pixel width of a wide line\r
- private static int N = 1; // Pixed width of a narrow line\r
-\r
- // Stores the actual narrow line width of the image being decoded.\r
- private int narrowLineWidth = -1;\r
-\r
- /**\r
- * Start/end guard pattern.\r
- *\r
- * Note: The end pattern is reversed because the row is reversed before\r
- * searching for the END_PATTERN\r
- */\r
- private static int[] START_PATTERN = {N, N, N, N};\r
- private static int[] END_PATTERN_REVERSED = {N, N, W};\r
-\r
- /**\r
- * Patterns of Wide / Narrow lines to indicate each digit\r
- */\r
- private static int[][] PATTERNS = new int[][]{\r
- new int[]{N, N, W, W, N}, // 0\r
- new int[]{W, N, N, N, W}, // 1\r
- new int[]{N, W, N, N, W}, // 2\r
- new int[]{W, W, N, N, N}, // 3\r
- new int[]{N, N, W, N, W}, // 4\r
- new int[]{W, N, W, N, N}, // 5\r
- new int[]{N, W, W, N, N}, // 6\r
- new int[]{N, N, N, W, W}, // 7\r
- new int[]{W, N, N, W, N}, // 8\r
- new int[]{N, W, N, W, N} // 9\r
- };\r
-\r
- public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) {\r
-\r
- StringBuilder result = new StringBuilder(20);\r
-\r
- // Find out where the Middle section (payload) starts & ends\r
- int[] startRange = decodeStart(row);\r
- int[] endRange = decodeEnd(row);\r
-\r
- decodeMiddle(row, startRange[1], endRange[0], result);\r
-\r
- string resultString = result.ToString();\r
-\r
- // To avoid false positives with 2D barcodes (and other patterns), make\r
- // an assumption that the decoded string must be 6, 10 or 14 digits.\r
- int length = resultString.Length;\r
- if (length != 6 && length != 10 && length != 14) {\r
- throw new ReaderException();\r
- }\r
-\r
- return new Result(\r
- resultString,\r
- null, // no natural byte representation for these barcodes\r
- new ResultPoint[] { new GenericResultPoint(startRange[1], (float) rowNumber),\r
- new GenericResultPoint(startRange[0], (float) rowNumber)},\r
- BarcodeFormat.ITF);\r
- }\r
-\r
- /**\r
- * @param row row of black/white values to search\r
- * @param payloadStart offset of start pattern\r
- * @param resultString {@link StringBuilder} to Append decoded chars to\r
- * @throws ReaderException if decoding could not complete successfully\r
- */\r
- static void decodeMiddle(BitArray row, int payloadStart, int payloadEnd, StringBuilder resultString) {\r
-\r
- // Digits are interleaved in pairs - 5 black lines for one digit, and the\r
- // 5\r
- // interleaved white lines for the second digit.\r
- // Therefore, need to scan 10 lines and then\r
- // split these into two arrays\r
- int[] counterDigitPair = new int[10];\r
- int[] counterBlack = new int[5];\r
- int[] counterWhite = new int[5];\r
-\r
- while (payloadStart < payloadEnd) {\r
-\r
- // Get 10 runs of black/white.\r
- recordPattern(row, payloadStart, counterDigitPair);\r
- // Split them into each array\r
- for (int k = 0; k < 5; k++) {\r
- int twoK = k << 1;\r
- counterBlack[k] = counterDigitPair[twoK];\r
- counterWhite[k] = counterDigitPair[twoK + 1];\r
- }\r
-\r
- int bestMatch = decodeDigit(counterBlack);\r
- resultString.Append((char) ('0' + bestMatch));\r
- bestMatch = decodeDigit(counterWhite);\r
- resultString.Append((char) ('0' + bestMatch));\r
-\r
- for (int i = 0; i < counterDigitPair.Length; i++) {\r
- payloadStart += counterDigitPair[i];\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Identify where the start of the middle / payload section starts.\r
- *\r
- * @param row row of black/white values to search\r
- * @return Array, containing index of start of 'start block' and end of\r
- * 'start block'\r
- * @throws ReaderException\r
- */\r
- int[] decodeStart(BitArray row) {\r
- int endStart = skipWhiteSpace(row);\r
- int[] startPattern = findGuardPattern(row, endStart, START_PATTERN);\r
-\r
- // Determine the width of a narrow line in pixels. We can do this by\r
- // getting the width of the start pattern and dividing by 4 because its\r
- // made up of 4 narrow lines.\r
- this.narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2;\r
-\r
- validateQuietZone(row, startPattern[0]);\r
-\r
- return startPattern;\r
- }\r
-\r
- /**\r
- * The start & end patterns must be pre/post fixed by a quiet zone. This\r
- * zone must be at least 10 times the width of a narrow line. Scan back until\r
- * we either get to the start of the barcode or match the necessary number of\r
- * quiet zone pixels.\r
- *\r
- * Note: Its assumed the row is reversed when using this method to find\r
- * quiet zone after the end pattern.\r
- *\r
- * ref: http://www.barcode-1.net/i25code.html\r
- *\r
- * @param row bit array representing the scanned barcode.\r
- * @param startPattern index into row of the start or end pattern.\r
- * @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown.\r
- */\r
- private void validateQuietZone(BitArray row, int startPattern) {\r
-\r
- int quietCount = this.narrowLineWidth * 10; // expect to find this many pixels of quiet zone\r
-\r
- for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {\r
- if (row.get(i)) {\r
- break;\r
- }\r
- quietCount--;\r
- }\r
- if (quietCount != 0) {\r
- // Unable to find the necessary number of quiet zone pixels.\r
- throw new ReaderException();\r
- }\r
- }\r
-\r
- /**\r
- * Skip all whitespace until we get to the first black line.\r
- *\r
- * @param row row of black/white values to search\r
- * @return index of the first black line.\r
- * @throws ReaderException Throws exception if no black lines are found in the row\r
- */\r
- private int skipWhiteSpace(BitArray row) {\r
- int width = row.getSize();\r
- int endStart = 0;\r
- while (endStart < width) {\r
- if (row.get(endStart)) {\r
- break;\r
- }\r
- endStart++;\r
- }\r
- if (endStart == width) {\r
- throw new ReaderException();\r
- }\r
-\r
- return endStart;\r
- }\r
-\r
- /**\r
- * Identify where the end of the middle / payload section ends.\r
- *\r
- * @param row row of black/white values to search\r
- * @return Array, containing index of start of 'end block' and end of 'end\r
- * block'\r
- * @throws ReaderException\r
- */\r
-\r
- int[] decodeEnd(BitArray row) {\r
-\r
- // For convenience, reverse the row and then\r
- // search from 'the start' for the end block\r
- row.reverse();\r
-\r
- int endStart = skipWhiteSpace(row);\r
- int[] endPattern;\r
- try {\r
- endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED);\r
- } catch (ReaderException e) {\r
- // Put our row of data back the right way before throwing\r
- row.reverse();\r
- throw e;\r
- }\r
-\r
- // The start & end patterns must be pre/post fixed by a quiet zone. This\r
- // zone must be at least 10 times the width of a narrow line.\r
- // ref: http://www.barcode-1.net/i25code.html\r
- validateQuietZone(row, endPattern[0]);\r
-\r
- // Now recalc the indicies of where the 'endblock' starts & stops to\r
- // accomodate\r
- // the reversed nature of the search\r
- int temp = endPattern[0];\r
- endPattern[0] = row.getSize() - endPattern[1];\r
- endPattern[1] = row.getSize() - temp;\r
-\r
- // Put the row back the righ way.\r
- row.reverse();\r
- return endPattern;\r
- }\r
-\r
- /**\r
- * @param row row of black/white values to search\r
- * @param rowOffset position to start search\r
- * @param pattern pattern of counts of number of black and white pixels that are\r
- * being searched for as a pattern\r
- * @return start/end horizontal offset of guard pattern, as an array of two\r
- * ints\r
- * @throws ReaderException if pattern is not found\r
- */\r
- int[] findGuardPattern(BitArray row, int rowOffset, int[] pattern) {\r
-\r
- // TODO: This is very similar to implementation in AbstractUPCEANReader. Consider if they can be merged to\r
- // a single method.\r
-\r
- int patternLength = pattern.Length;\r
- int[] counters = new int[patternLength];\r
- int width = row.getSize();\r
- bool isWhite = false;\r
-\r
- int counterPosition = 0;\r
- int patternStart = rowOffset;\r
- for (int x = rowOffset; x < width; x++) {\r
- bool pixel = row.get(x);\r
- if ((!pixel && isWhite) || (pixel && !isWhite)) {\r
- counters[counterPosition]++;\r
- } else {\r
- if (counterPosition == patternLength - 1) {\r
- if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {\r
- return new int[]{patternStart, x};\r
- }\r
- patternStart += counters[0] + counters[1];\r
- for (int y = 2; y < patternLength; y++) {\r
- counters[y - 2] = counters[y];\r
- }\r
- counters[patternLength - 2] = 0;\r
- counters[patternLength - 1] = 0;\r
- counterPosition--;\r
- } else {\r
- counterPosition++;\r
- }\r
- counters[counterPosition] = 1;\r
- isWhite = !isWhite;\r
- }\r
- }\r
- throw new ReaderException();\r
- }\r
-\r
- /**\r
- * Attempts to decode a sequence of ITF black/white lines into single\r
- * digit.\r
- *\r
- * @param counters the counts of runs of observed black/white/black/... values\r
- * @return The decoded digit\r
- * @throws ReaderException if digit cannot be decoded\r
- */\r
- private static int decodeDigit(int[] counters) {\r
-\r
- int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept\r
- int bestMatch = -1;\r
- int max = PATTERNS.Length;\r
- for (int i = 0; i < max; i++) {\r
- int[] pattern = PATTERNS[i];\r
- int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);\r
- if (variance < bestVariance) {\r
- bestVariance = variance;\r
- bestMatch = i;\r
- }\r
- }\r
- if (bestMatch >= 0) {\r
- return bestMatch;\r
- } else {\r
- throw new ReaderException();\r
- }\r
- }\r
- \r
- }\r
+ \r
+ /// <summary> <p>Implements decoding of the ITF format.</p>\r
+ /// \r
+ /// <p>"ITF" stands for Interleaved Two of Five. This Reader will scan ITF barcode with 6, 10 or 14\r
+ /// digits. The checksum is optional and is not applied by this Reader. The consumer of the decoded\r
+ /// value will have to apply a checksum if required.</p>\r
+ /// \r
+ /// <p><a href="http://en.wikipedia.org/wiki/Interleaved_2_of_5">http://en.wikipedia.org/wiki/Interleaved_2_of_5</a>\r
+ /// is a great reference for Interleaved 2 of 5 information.</p>\r
+ /// \r
+ /// </summary>\r
+ /// <author> kevin.osullivan@sita.aero, SITA Lab.\r
+ /// </author>\r
+ /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
+ /// </author>\r
+ public sealed class ITFReader:OneDReader\r
+ {\r
+ \r
+ //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_AVG_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+ //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'"\r
+ private static readonly int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f);\r
+ //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_INDIVIDUAL_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+ //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'"\r
+ private static readonly int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8f);\r
+ \r
+ private const int W = 3; // Pixel width of a wide line\r
+ private const int N = 1; // Pixed width of a narrow line\r
+ \r
+ //UPGRADE_NOTE: Final was removed from the declaration of 'DEFAULT_ALLOWED_LENGTHS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+ private static readonly int[] DEFAULT_ALLOWED_LENGTHS = new int[]{6, 10, 14, 44};\r
+ \r
+ // Stores the actual narrow line width of the image being decoded.\r
+ private int narrowLineWidth = - 1;\r
+ \r
+ /// <summary> Start/end guard pattern.\r
+ /// \r
+ /// Note: The end pattern is reversed because the row is reversed before\r
+ /// searching for the END_PATTERN\r
+ /// </summary>\r
+ //UPGRADE_NOTE: Final was removed from the declaration of 'START_PATTERN '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+ private static readonly int[] START_PATTERN = new int[]{N, N, N, N};\r
+ //UPGRADE_NOTE: Final was removed from the declaration of 'END_PATTERN_REVERSED '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+ private static readonly int[] END_PATTERN_REVERSED = new int[]{N, N, W};\r
+ \r
+ /// <summary> Patterns of Wide / Narrow lines to indicate each digit</summary>\r
+ //UPGRADE_NOTE: Final was removed from the declaration of 'PATTERNS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+ private static readonly int[][] PATTERNS = new int[][]{new int[]{N, N, W, W, N}, new int[]{W, N, N, N, W}, new int[]{N, W, N, N, W}, new int[]{W, W, N, N, N}, new int[]{N, N, W, N, W}, new int[]{W, N, W, N, N}, new int[]{N, W, W, N, N}, new int[]{N, N, N, W, W}, new int[]{W, N, N, W, N}, new int[]{N, W, N, W, N}};\r
+ \r
+ public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints)\r
+ {\r
+ \r
+ // Find out where the Middle section (payload) starts & ends\r
+ int[] startRange = decodeStart(row);\r
+ int[] endRange = decodeEnd(row);\r
+ \r
+ System.Text.StringBuilder result = new System.Text.StringBuilder(20);\r
+ decodeMiddle(row, startRange[1], endRange[0], result);\r
+ System.String resultString = result.ToString();\r
+ \r
+ int[] allowedLengths = null;\r
+ if (hints != null)\r
+ {\r
+ allowedLengths = (int[]) hints[DecodeHintType.ALLOWED_LENGTHS];\r
+ }\r
+ if (allowedLengths == null)\r
+ {\r
+ allowedLengths = DEFAULT_ALLOWED_LENGTHS;\r
+ }\r
+ \r
+ // To avoid false positives with 2D barcodes (and other patterns), make\r
+ // an assumption that the decoded string must be 6, 10 or 14 digits.\r
+ int length = resultString.Length;\r
+ bool lengthOK = false;\r
+ for (int i = 0; i < allowedLengths.Length; i++)\r
+ {\r
+ if (length == allowedLengths[i])\r
+ {\r
+ lengthOK = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!lengthOK)\r
+ {\r
+ throw ReaderException.Instance;\r
+ }\r
+ \r
+ //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'"\r
+ return new Result(resultString, null, new ResultPoint[]{new ResultPoint(startRange[1], (float) rowNumber), new ResultPoint(endRange[0], (float) rowNumber)}, BarcodeFormat.ITF);\r
+ }\r
+ \r
+ /// <param name="row"> row of black/white values to search\r
+ /// </param>\r
+ /// <param name="payloadStart">offset of start pattern\r
+ /// </param>\r
+ /// <param name="resultString">{@link StringBuffer} to append decoded chars to\r
+ /// </param>\r
+ /// <throws> ReaderException if decoding could not complete successfully </throws>\r
+ private static void decodeMiddle(BitArray row, int payloadStart, int payloadEnd, System.Text.StringBuilder resultString)\r
+ {\r
+ \r
+ // Digits are interleaved in pairs - 5 black lines for one digit, and the\r
+ // 5\r
+ // interleaved white lines for the second digit.\r
+ // Therefore, need to scan 10 lines and then\r
+ // split these into two arrays\r
+ int[] counterDigitPair = new int[10];\r
+ int[] counterBlack = new int[5];\r
+ int[] counterWhite = new int[5];\r
+ \r
+ while (payloadStart < payloadEnd)\r
+ {\r
+ \r
+ // Get 10 runs of black/white.\r
+ recordPattern(row, payloadStart, counterDigitPair);\r
+ // Split them into each array\r
+ for (int k = 0; k < 5; k++)\r
+ {\r
+ int twoK = k << 1;\r
+ counterBlack[k] = counterDigitPair[twoK];\r
+ counterWhite[k] = counterDigitPair[twoK + 1];\r
+ }\r
+ \r
+ int bestMatch = decodeDigit(counterBlack);\r
+ resultString.Append((char) ('0' + bestMatch));\r
+ bestMatch = decodeDigit(counterWhite);\r
+ resultString.Append((char) ('0' + bestMatch));\r
+ \r
+ for (int i = 0; i < counterDigitPair.Length; i++)\r
+ {\r
+ payloadStart += counterDigitPair[i];\r
+ }\r
+ }\r
+ }\r
+ \r
+ /// <summary> Identify where the start of the middle / payload section starts.\r
+ /// \r
+ /// </summary>\r
+ /// <param name="row">row of black/white values to search\r
+ /// </param>\r
+ /// <returns> Array, containing index of start of 'start block' and end of\r
+ /// 'start block'\r
+ /// </returns>\r
+ /// <throws> ReaderException </throws>\r
+ internal int[] decodeStart(BitArray row)\r
+ {\r
+ int endStart = skipWhiteSpace(row);\r
+ int[] startPattern = findGuardPattern(row, endStart, START_PATTERN);\r
+ \r
+ // Determine the width of a narrow line in pixels. We can do this by\r
+ // getting the width of the start pattern and dividing by 4 because its\r
+ // made up of 4 narrow lines.\r
+ this.narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2;\r
+ \r
+ validateQuietZone(row, startPattern[0]);\r
+ \r
+ return startPattern;\r
+ }\r
+ \r
+ /// <summary> The start & end patterns must be pre/post fixed by a quiet zone. This\r
+ /// zone must be at least 10 times the width of a narrow line. Scan back until\r
+ /// we either get to the start of the barcode or match the necessary number of\r
+ /// quiet zone pixels.\r
+ /// \r
+ /// Note: Its assumed the row is reversed when using this method to find\r
+ /// quiet zone after the end pattern.\r
+ /// \r
+ /// ref: http://www.barcode-1.net/i25code.html\r
+ /// \r
+ /// </summary>\r
+ /// <param name="row">bit array representing the scanned barcode.\r
+ /// </param>\r
+ /// <param name="startPattern">index into row of the start or end pattern.\r
+ /// </param>\r
+ /// <throws> ReaderException if the quiet zone cannot be found, a ReaderException is thrown. </throws>\r
+ private void validateQuietZone(BitArray row, int startPattern)\r
+ {\r
+ \r
+ int quietCount = this.narrowLineWidth * 10; // expect to find this many pixels of quiet zone\r
+ \r
+ for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--)\r
+ {\r
+ if (row.get_Renamed(i))\r
+ {\r
+ break;\r
+ }\r
+ quietCount--;\r
+ }\r
+ if (quietCount != 0)\r
+ {\r
+ // Unable to find the necessary number of quiet zone pixels.\r
+ throw ReaderException.Instance;\r
+ }\r
+ }\r
+ \r
+ /// <summary> Skip all whitespace until we get to the first black line.\r
+ /// \r
+ /// </summary>\r
+ /// <param name="row">row of black/white values to search\r
+ /// </param>\r
+ /// <returns> index of the first black line.\r
+ /// </returns>\r
+ /// <throws> ReaderException Throws exception if no black lines are found in the row </throws>\r
+ private static int skipWhiteSpace(BitArray row)\r
+ {\r
+ int width = row.Size;\r
+ int endStart = 0;\r
+ while (endStart < width)\r
+ {\r
+ if (row.get_Renamed(endStart))\r
+ {\r
+ break;\r
+ }\r
+ endStart++;\r
+ }\r
+ if (endStart == width)\r
+ {\r
+ throw ReaderException.Instance;\r
+ }\r
+ \r
+ return endStart;\r
+ }\r
+ \r
+ /// <summary> Identify where the end of the middle / payload section ends.\r
+ /// \r
+ /// </summary>\r
+ /// <param name="row">row of black/white values to search\r
+ /// </param>\r
+ /// <returns> Array, containing index of start of 'end block' and end of 'end\r
+ /// block'\r
+ /// </returns>\r
+ /// <throws> ReaderException </throws>\r
+ \r
+ internal int[] decodeEnd(BitArray row)\r
+ {\r
+ \r
+ // For convenience, reverse the row and then\r
+ // search from 'the start' for the end block\r
+ row.reverse();\r
+ try\r
+ {\r
+ int endStart = skipWhiteSpace(row);\r
+ int[] endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED);\r
+ \r
+ // The start & end patterns must be pre/post fixed by a quiet zone. This\r
+ // zone must be at least 10 times the width of a narrow line.\r
+ // ref: http://www.barcode-1.net/i25code.html\r
+ validateQuietZone(row, endPattern[0]);\r
+ \r
+ // Now recalculate the indices of where the 'endblock' starts & stops to\r
+ // accommodate\r
+ // the reversed nature of the search\r
+ int temp = endPattern[0];\r
+ endPattern[0] = row.Size - endPattern[1];\r
+ endPattern[1] = row.Size - temp;\r
+ \r
+ return endPattern;\r
+ }\r
+ finally\r
+ {\r
+ // Put the row back the right way.\r
+ row.reverse();\r
+ }\r
+ }\r
+ \r
+ /// <param name="row"> row of black/white values to search\r
+ /// </param>\r
+ /// <param name="rowOffset">position to start search\r
+ /// </param>\r
+ /// <param name="pattern"> pattern of counts of number of black and white pixels that are\r
+ /// being searched for as a pattern\r
+ /// </param>\r
+ /// <returns> start/end horizontal offset of guard pattern, as an array of two\r
+ /// ints\r
+ /// </returns>\r
+ /// <throws> ReaderException if pattern is not found </throws>\r
+ private static int[] findGuardPattern(BitArray row, int rowOffset, int[] pattern)\r
+ {\r
+ \r
+ // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be\r
+ // merged to a single method.\r
+ int patternLength = pattern.Length;\r
+ int[] counters = new int[patternLength];\r
+ int width = row.Size;\r
+ bool isWhite = false;\r
+ \r
+ int counterPosition = 0;\r
+ int patternStart = rowOffset;\r
+ for (int x = rowOffset; x < width; x++)\r
+ {\r
+ bool pixel = row.get_Renamed(x);\r
+ if (pixel ^ isWhite)\r
+ {\r
+ counters[counterPosition]++;\r
+ }\r
+ else\r
+ {\r
+ if (counterPosition == patternLength - 1)\r
+ {\r
+ if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE)\r
+ {\r
+ return new int[]{patternStart, x};\r
+ }\r
+ patternStart += counters[0] + counters[1];\r
+ for (int y = 2; y < patternLength; y++)\r
+ {\r
+ counters[y - 2] = counters[y];\r
+ }\r
+ counters[patternLength - 2] = 0;\r
+ counters[patternLength - 1] = 0;\r
+ counterPosition--;\r
+ }\r
+ else\r
+ {\r
+ counterPosition++;\r
+ }\r
+ counters[counterPosition] = 1;\r
+ isWhite = !isWhite;\r
+ }\r
+ }\r
+ throw ReaderException.Instance;\r
+ }\r
+ \r
+ /// <summary> Attempts to decode a sequence of ITF black/white lines into single\r
+ /// digit.\r
+ /// \r
+ /// </summary>\r
+ /// <param name="counters">the counts of runs of observed black/white/black/... values\r
+ /// </param>\r
+ /// <returns> The decoded digit\r
+ /// </returns>\r
+ /// <throws> ReaderException if digit cannot be decoded </throws>\r
+ private static int decodeDigit(int[] counters)\r
+ {\r
+ \r
+ int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept\r
+ int bestMatch = - 1;\r
+ int max = PATTERNS.Length;\r
+ for (int i = 0; i < max; i++)\r
+ {\r
+ int[] pattern = PATTERNS[i];\r
+ int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);\r
+ if (variance < bestVariance)\r
+ {\r
+ bestVariance = variance;\r
+ bestMatch = i;\r
+ }\r
+ }\r
+ if (bestMatch >= 0)\r
+ {\r
+ return bestMatch;\r
+ }\r
+ else\r
+ {\r
+ throw ReaderException.Instance;\r
+ }\r
+ }\r
+ }\r
}
\ No newline at end of file