Prettify scan result screen, I think
[zxing.git] / csharp / oned / Code128Reader.cs
index 9834a85..e7dc192 100755 (executable)
@@ -1,4 +1,6 @@
-/*\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 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
-    using System;\r
-    using System.Text;\r
-    using com.google.zxing.common;\r
-\r
-\r
-    public sealed class Code128Reader : AbstractOneDReader\r
-    { \r
-            private static  int[][] CODE_PATTERNS = new int[][]{\r
-              new int[]{2, 1, 2, 2, 2, 2}, // 0\r
-              new int[]{2, 2, 2, 1, 2, 2},\r
-              new int[]{2, 2, 2, 2, 2, 1},\r
-              new int[]{1, 2, 1, 2, 2, 3},\r
-              new int[]{1, 2, 1, 3, 2, 2},\r
-              new int[]{1, 3, 1, 2, 2, 2}, // 5\r
-              new int[]{1, 2, 2, 2, 1, 3},\r
-              new int[]{1, 2, 2, 3, 1, 2},\r
-              new int[]{1, 3, 2, 2, 1, 2},\r
-              new int[]{2, 2, 1, 2, 1, 3},\r
-              new int[]{2, 2, 1, 3, 1, 2}, // 10\r
-              new int[]{2, 3, 1, 2, 1, 2},\r
-              new int[]{1, 1, 2, 2, 3, 2},\r
-              new int[]{1, 2, 2, 1, 3, 2},\r
-              new int[]{1, 2, 2, 2, 3, 1},\r
-              new int[]{1, 1, 3, 2, 2, 2}, // 15\r
-              new int[]{1, 2, 3, 1, 2, 2},\r
-              new int[]{1, 2, 3, 2, 2, 1},\r
-              new int[]{2, 2, 3, 2, 1, 1},\r
-              new int[]{2, 2, 1, 1, 3, 2},\r
-              new int[]{2, 2, 1, 2, 3, 1}, // 20\r
-              new int[]{2, 1, 3, 2, 1, 2},\r
-              new int[]{2, 2, 3, 1, 1, 2},\r
-              new int[]{3, 1, 2, 1, 3, 1},\r
-              new int[]{3, 1, 1, 2, 2, 2},\r
-              new int[]{3, 2, 1, 1, 2, 2}, // 25\r
-              new int[]{3, 2, 1, 2, 2, 1},\r
-              new int[]{3, 1, 2, 2, 1, 2},\r
-              new int[]{3, 2, 2, 1, 1, 2},\r
-              new int[]{3, 2, 2, 2, 1, 1},\r
-              new int[]{2, 1, 2, 1, 2, 3}, // 30\r
-              new int[]{2, 1, 2, 3, 2, 1},\r
-              new int[]{2, 3, 2, 1, 2, 1},\r
-              new int[]{1, 1, 1, 3, 2, 3},\r
-              new int[]{1, 3, 1, 1, 2, 3},\r
-              new int[]{1, 3, 1, 3, 2, 1}, // 35\r
-              new int[]{1, 1, 2, 3, 1, 3},\r
-              new int[]{1, 3, 2, 1, 1, 3},\r
-              new int[]{1, 3, 2, 3, 1, 1},\r
-              new int[]{2, 1, 1, 3, 1, 3},\r
-              new int[]{2, 3, 1, 1, 1, 3}, // 40\r
-              new int[]{2, 3, 1, 3, 1, 1},\r
-              new int[]{1, 1, 2, 1, 3, 3},\r
-              new int[]{1, 1, 2, 3, 3, 1},\r
-              new int[]{1, 3, 2, 1, 3, 1},\r
-              new int[]{1, 1, 3, 1, 2, 3}, // 45\r
-              new int[]{1, 1, 3, 3, 2, 1},\r
-              new int[]{1, 3, 3, 1, 2, 1},\r
-              new int[]{3, 1, 3, 1, 2, 1},\r
-              new int[]{2, 1, 1, 3, 3, 1},\r
-              new int[]{2, 3, 1, 1, 3, 1}, // 50\r
-              new int[]{2, 1, 3, 1, 1, 3},\r
-              new int[]{2, 1, 3, 3, 1, 1},\r
-              new int[]{2, 1, 3, 1, 3, 1},\r
-              new int[]{3, 1, 1, 1, 2, 3},\r
-              new int[]{3, 1, 1, 3, 2, 1}, // 55\r
-              new int[]{3, 3, 1, 1, 2, 1},\r
-              new int[]{3, 1, 2, 1, 1, 3},\r
-              new int[]{3, 1, 2, 3, 1, 1},\r
-              new int[]{3, 3, 2, 1, 1, 1},\r
-              new int[]{3, 1, 4, 1, 1, 1}, // 60\r
-              new int[]{2, 2, 1, 4, 1, 1},\r
-              new int[]{4, 3, 1, 1, 1, 1},\r
-              new int[]{1, 1, 1, 2, 2, 4},\r
-              new int[]{1, 1, 1, 4, 2, 2},\r
-              new int[] {1, 2, 1, 1, 2, 4}, // 65\r
-              new int[]{1, 2, 1, 4, 2, 1},\r
-              new int[]{1, 4, 1, 1, 2, 2},\r
-              new int[]{1, 4, 1, 2, 2, 1},\r
-              new int[]{1, 1, 2, 2, 1, 4},\r
-              new int[]{1, 1, 2, 4, 1, 2}, // 70\r
-              new int[]{1, 2, 2, 1, 1, 4},\r
-              new int[]{1, 2, 2, 4, 1, 1},\r
-              new int[]{1, 4, 2, 1, 1, 2},\r
-              new int[]{1, 4, 2, 2, 1, 1},\r
-              new int[]{2, 4, 1, 2, 1, 1}, // 75\r
-              new int[]{2, 2, 1, 1, 1, 4},\r
-              new int[]{4, 1, 3, 1, 1, 1},\r
-              new int[]{2, 4, 1, 1, 1, 2},\r
-              new int[]{1, 3, 4, 1, 1, 1},\r
-              new int[]{1, 1, 1, 2, 4, 2}, // 80\r
-              new int[]{1, 2, 1, 1, 4, 2},\r
-              new int[]{1, 2, 1, 2, 4, 1},\r
-              new int[]{1, 1, 4, 2, 1, 2},\r
-              new int[]{1, 2, 4, 1, 1, 2},\r
-              new int[]{1, 2, 4, 2, 1, 1}, // 85\r
-              new int[]{4, 1, 1, 2, 1, 2},\r
-              new int[]{4, 2, 1, 1, 1, 2},\r
-              new int[]{4, 2, 1, 2, 1, 1},\r
-              new int[]{2, 1, 2, 1, 4, 1},\r
-              new int[]{2, 1, 4, 1, 2, 1}, // 90\r
-              new int[]{4, 1, 2, 1, 2, 1},\r
-              new int[]{1, 1, 1, 1, 4, 3},\r
-              new int[]{1, 1, 1, 3, 4, 1},\r
-              new int[]{1, 3, 1, 1, 4, 1},\r
-              new int[]{1, 1, 4, 1, 1, 3}, // 95\r
-              new int[]{1, 1, 4, 3, 1, 1},\r
-              new int[]{4, 1, 1, 1, 1, 3},\r
-              new int[]{4, 1, 1, 3, 1, 1},\r
-              new int[]{1, 1, 3, 1, 4, 1},\r
-              new int[]{1, 1, 4, 1, 3, 1}, // 100\r
-              new int[]{3, 1, 1, 1, 4, 1},\r
-              new int[]{4, 1, 1, 1, 3, 1},\r
-              new int[]{2, 1, 1, 4, 1, 2},\r
-              new int[]{2, 1, 1, 2, 1, 4},\r
-              new int[]{2, 1, 1, 2, 3, 2}, // 105\r
-              new int[]{2, 3, 3, 1, 1, 1, 2}\r
-          };\r
-\r
-      private static  int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f);\r
-      private static  int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f);\r
-\r
-      private const int CODE_SHIFT = 98;\r
-\r
-      private const int CODE_CODE_C = 99;\r
-      private const int CODE_CODE_B = 100;\r
-      private const int CODE_CODE_A = 101;\r
-\r
-      private const int CODE_FNC_1 = 102;\r
-      private const int CODE_FNC_2 = 97;\r
-      private const int CODE_FNC_3 = 96;\r
-      private const int CODE_FNC_4_A = 101;\r
-      private const int CODE_FNC_4_B = 100;\r
-\r
-      private const  int CODE_START_A = 103;\r
-      private const int CODE_START_B = 104;\r
-      private const int CODE_START_C = 105;\r
-      private const int CODE_STOP = 106;\r
-\r
-      private static int[] findStartPattern(BitArray row) {\r
-        int width = row.getSize();\r
-        int rowOffset = 0;\r
-        while (rowOffset < width) {\r
-          if (row.get(rowOffset)) {\r
-            break;\r
-          }\r
-          rowOffset++;\r
-        }\r
-\r
-        int counterPosition = 0;\r
-        int[] counters = new int[6];\r
-        int patternStart = rowOffset;\r
-        bool isWhite = false;\r
-        int patternLength = counters.Length;\r
-\r
-        for (int i = rowOffset; i < width; i++) {\r
-          bool pixel = row.get(i);\r
-          if ((!pixel && isWhite) || (pixel && !isWhite)) {\r
-            counters[counterPosition]++;\r
-          } else {\r
-            if (counterPosition == patternLength - 1) {\r
-              int bestVariance = MAX_AVG_VARIANCE;\r
-              int bestMatch = -1;\r
-              for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {\r
-                int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE);\r
-                if (variance < bestVariance) {\r
-                  bestVariance = variance;\r
-                  bestMatch = startCode;\r
-                }\r
-              }\r
-              if (bestMatch >= 0) {\r
-                // Look for whitespace before start pattern, >= 50% of width of start pattern            \r
-                if (row.isRange(Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false)) {\r
-                  return new int[]{patternStart, i, bestMatch};\r
-                }\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
-      private static int decodeCode(BitArray row, int[] counters, int rowOffset) {\r
-        recordPattern(row, rowOffset, counters);\r
-        int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept\r
-        int bestMatch = -1;\r
-        for (int d = 0; d < CODE_PATTERNS.Length; d++) {\r
-          int[] pattern = CODE_PATTERNS[d];\r
-          int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);\r
-          if (variance < bestVariance) {\r
-            bestVariance = variance;\r
-            bestMatch = d;\r
-          }\r
-        }\r
-        // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6\r
-        if (bestMatch >= 0) {\r
-          return bestMatch;\r
-        } else {\r
-          throw new ReaderException();\r
-        }\r
-      }\r
-\r
-      public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) {\r
-\r
-        int[] startPatternInfo = findStartPattern(row);\r
-        int startCode = startPatternInfo[2];\r
-        int codeSet;\r
-        switch (startCode) {\r
-          case CODE_START_A:\r
-            codeSet = CODE_CODE_A;\r
-            break;\r
-          case CODE_START_B:\r
-            codeSet = CODE_CODE_B;\r
-            break;\r
-          case CODE_START_C:\r
-            codeSet = CODE_CODE_C;\r
-            break;\r
-          default:\r
-            throw new ReaderException();\r
-        }\r
-\r
-        bool done = false;\r
-        bool isNextShifted = false;\r
-\r
-        StringBuilder result = new StringBuilder();\r
-        int lastStart = startPatternInfo[0];\r
-        int nextStart = startPatternInfo[1];\r
-        int[] counters = new int[6];\r
-\r
-        int lastCode = 0;\r
-        int code = 0;\r
-        int checksumTotal = startCode;\r
-        int multiplier = 0;\r
-        bool lastCharacterWasPrintable = true;\r
-\r
-        while (!done) {\r
-\r
-          bool unshift = isNextShifted;\r
-          isNextShifted = false;\r
-\r
-          // Save off last code\r
-          lastCode = code;\r
-\r
-          // Decode another code from image\r
-          code = decodeCode(row, counters, nextStart);\r
-\r
-          // Remember whether the last code was printable or not (excluding CODE_STOP)\r
-          if (code != CODE_STOP) {\r
-            lastCharacterWasPrintable = true;\r
-          }\r
-\r
-          // Add to checksum computation (if not CODE_STOP of course)\r
-          if (code != CODE_STOP) {\r
-            multiplier++;\r
-            checksumTotal += multiplier * code;\r
-          }\r
-\r
-          // Advance to where the next code will to start\r
-          lastStart = nextStart;\r
-          for (int i = 0; i < counters.Length; i++) {\r
-            nextStart += counters[i];\r
-          }\r
-\r
-          // Take care of illegal start codes\r
-          switch (code) {\r
-            case CODE_START_A:\r
-            case CODE_START_B:\r
-            case CODE_START_C:\r
-              throw new ReaderException();\r
-          }\r
-\r
-          switch (codeSet) {\r
-\r
-            case CODE_CODE_A:\r
-              if (code < 64) {\r
-                result.Append((char) (' ' + code));\r
-              } else if (code < 96) {\r
-                result.Append((char) (code - 64));\r
-              } else {\r
-                // Don't let CODE_STOP, which always appears, affect whether whether we think the last code\r
-                // was printable or not\r
-                if (code != CODE_STOP) {\r
-                  lastCharacterWasPrintable = false;\r
-                }\r
-                switch (code) {\r
-                  case CODE_FNC_1:\r
-                  case CODE_FNC_2:\r
-                  case CODE_FNC_3:\r
-                  case CODE_FNC_4_A:\r
-                    // do nothing?\r
-                    break;\r
-                  case CODE_SHIFT:\r
-                    isNextShifted = true;\r
-                    codeSet = CODE_CODE_B;\r
-                    break;\r
-                  case CODE_CODE_B:\r
-                    codeSet = CODE_CODE_B;\r
-                    break;\r
-                  case CODE_CODE_C:\r
-                    codeSet = CODE_CODE_C;\r
-                    break;\r
-                  case CODE_STOP:\r
-                    done = true;\r
-                    break;\r
-                }\r
-              }\r
-              break;\r
-            case CODE_CODE_B:\r
-              if (code < 96) {\r
-                result.Append((char) (' ' + code));\r
-              } else {\r
-                if (code != CODE_STOP) {\r
-                  lastCharacterWasPrintable = false;\r
-                }\r
-                switch (code) {\r
-                  case CODE_FNC_1:\r
-                  case CODE_FNC_2:\r
-                  case CODE_FNC_3:\r
-                  case CODE_FNC_4_B:\r
-                    // do nothing?\r
-                    break;\r
-                  case CODE_SHIFT:\r
-                    isNextShifted = true;\r
-                    codeSet = CODE_CODE_C;\r
-                    break;\r
-                  case CODE_CODE_A:\r
-                    codeSet = CODE_CODE_A;\r
-                    break;\r
-                  case CODE_CODE_C:\r
-                    codeSet = CODE_CODE_C;\r
-                    break;\r
-                  case CODE_STOP:\r
-                    done = true;\r
-                    break;\r
-                }\r
-              }\r
-              break;\r
-            case CODE_CODE_C:\r
-              if (code < 100) {\r
-                if (code < 10) {\r
-                  result.Append('0');\r
-                }\r
-                result.Append(code);\r
-              } else {\r
-                if (code != CODE_STOP) {\r
-                  lastCharacterWasPrintable = false;\r
-                }\r
-                switch (code) {\r
-                  case CODE_FNC_1:\r
-                    // do nothing?\r
-                    break;\r
-                  case CODE_CODE_A:\r
-                    codeSet = CODE_CODE_A;\r
-                    break;\r
-                  case CODE_CODE_B:\r
-                    codeSet = CODE_CODE_B;\r
-                    break;\r
-                  case CODE_STOP:\r
-                    done = true;\r
-                    break;\r
-                }\r
-              }\r
-              break;\r
-          }\r
-\r
-          // Unshift back to another code set if we were shifted\r
-          if (unshift) {\r
-            switch (codeSet) {\r
-              case CODE_CODE_A:\r
-                codeSet = CODE_CODE_C;\r
-                break;\r
-              case CODE_CODE_B:\r
-                codeSet = CODE_CODE_A;\r
-                break;\r
-              case CODE_CODE_C:\r
-                codeSet = CODE_CODE_B;\r
-                break;\r
-            }\r
-          }\r
-\r
-        }\r
-\r
-        // Check for ample whitespice following pattern, but, to do this we first need to remember that we\r
-        // fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left to read off.\r
-        // Would be slightly better to properly read. Here we just skip it:\r
-        while (row.get(nextStart)) {\r
-          nextStart++;\r
-        }\r
-        if (!row.isRange(nextStart, Math.Min(row.getSize(), nextStart + (nextStart - lastStart) / 2), false)) {\r
-          throw new ReaderException();\r
-        }\r
-\r
-        // Pull out from sum the value of the penultimate check code\r
-        checksumTotal -= multiplier * lastCode;\r
-        // lastCode is the checksum then:\r
-        if (checksumTotal % 103 != lastCode) {\r
-          throw new ReaderException();\r
-        }\r
-\r
-        // Need to pull out the check digits from string\r
-        int resultLength = result.Length;\r
-        // Only bother if, well, the result had at least one character, and if the checksum digit happened\r
-        // to be a printable character. If it was just interpreted as a control code, nothing to remove\r
-        if (resultLength > 0 && lastCharacterWasPrintable) {\r
-          if (codeSet == CODE_CODE_C) {\r
-              result.Remove(resultLength - 2, resultLength);\r
-          } else {\r
-              result.Remove(resultLength - 1, resultLength);\r
-          }\r
-        }\r
-\r
-        String resultString = result.ToString();\r
-\r
-        if (resultString.Length == 0) {\r
-          // Almost surely a false positive\r
-          throw new ReaderException();\r
-        }\r
-\r
-        float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;\r
-        float right = (float) (nextStart + lastStart) / 2.0f;\r
-        return new Result(\r
-            resultString,\r
-            null,\r
-            new ResultPoint[]{\r
-                new GenericResultPoint(left, (float) rowNumber),\r
-                new GenericResultPoint(right, (float) rowNumber)},\r
-            BarcodeFormat.CODE_128);\r
-\r
-      }\r
-    \r
-    }\r
-\r
-\r
-\r
+{\r
+       \r
+       /// <summary> <p>Decodes Code 128 barcodes.</p>\r
+       /// \r
+       /// </summary>\r
+       /// <author>  Sean Owen\r
+       /// </author>\r
+       /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
+       /// </author>\r
+       public sealed class Code128Reader:OneDReader\r
+       {\r
+               \r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'CODE_PATTERNS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private static readonly int[][] CODE_PATTERNS = new int[][]{new int[]{2, 1, 2, 2, 2, 2}, new int[]{2, 2, 2, 1, 2, 2}, new int[]{2, 2, 2, 2, 2, 1}, new int[]{1, 2, 1, 2, 2, 3}, new int[]{1, 2, 1, 3, 2, 2}, new int[]{1, 3, 1, 2, 2, 2}, new int[]{1, 2, 2, 2, 1, 3}, new int[]{1, 2, 2, 3, 1, 2}, new int[]{1, 3, 2, 2, 1, 2}, new int[]{2, 2, 1, 2, 1, 3}, new int[]{2, 2, 1, 3, 1, 2}, new int[]{2, 3, 1, 2, 1, 2}, new int[]{1, 1, 2, 2, 3, 2}, new int[]{1, 2, 2, 1, 3, 2}, new int[]{1, 2, 2, 2, 3, 1}, new int[]{1, 1, 3, 2, 2, 2}, new int[]{1, 2, 3, 1, 2, 2}, new int[]{1, 2, 3, 2, 2, 1}, new int[]{2, 2, 3, 2, 1, 1}, new int[]{2, 2, 1, 1, 3, 2}, new int[]{2, 2, 1, 2, 3, 1}, new int[]{2, 1, 3, 2, 1, 2}, new int[]{2, 2, 3, 1, 1, 2}, new int[]{3, 1, 2, 1, 3, 1}, new int[]{3, 1, 1, 2, 2, 2}, new int[]{3, 2, 1, 1, 2, 2}, new int[]{3, 2, 1, 2, 2, 1}, new int[]{3, 1, 2, 2, 1, 2}, new int[]{3, 2, 2, 1, 1, 2}, new int[]{3, 2, 2, 2, 1, 1}, new int[]{2, 1, 2, 1, 2, 3}, new int[]{2, 1, 2, 3, 2, 1}, new int[]{2, 3, 2, 1, 2, 1}, new int[]{1, 1, 1, 3, 2, 3}, new int[]{1, 3, 1, 1, 2, 3}, new int[]{1, 3, 1, 3, 2, 1}, new int[]{1, 1, 2, 3, 1, 3}, new int[]{1, 3, 2, 1, 1, 3}, new int[]{1, 3, 2, 3, 1, 1}, new int[]{2, 1, 1, 3, 1, 3}, new int[]{2, 3, 1, 1, 1, 3}, new int[]{2, 3, 1, 3, 1, 1}, new int[]{1, 1, 2, 1, 3, 3}, new int[]{1, 1, 2, 3, 3, 1}, new int[]{1, 3, 2, 1, 3, 1}, new int[]{1, 1, 3, 1, 2, 3}, new int[]{1, 1, 3, 3, 2, 1}, new int[]{1, 3, 3, 1, 2, 1}, new int[]{3, 1, 3, 1, 2, 1}, new int[]{2, 1, 1, 3, 3, 1}, new int[]{2, 3, 1, 1, 3, 1}, new int[]{2, 1, 3, 1, 1, 3}, new int[]{2, 1, 3, 3, 1, 1}, new int[]{2, 1, 3, 1, 3, 1}, new int[]{3, 1, 1, 1, 2, 3}, new int[]{3, 1, 1, 3, 2, 1}, new int[]{3, 3, 1, 1, 2, 1}, new int[]{3, 1, 2, 1, 1, 3}, new int[]{3, 1, 2, 3, 1, 1}, new int[]{3, 3, 2, 1, 1, 1}, new int[]{3, 1, 4, 1, 1, 1}, new int[]{2, 2, 1, 4, 1, 1}, new int[]{4, 3, 1, 1, 1, 1}, new int[]{1, 1, 1, 2, 2, 4}, new int[]{1, 1, 1, 4, 2, 2}, new int[]{1, 2, 1, 1, 2, 4}, new int[]{1, 2, 1, 4, 2, 1}, new int[]{1, 4, 1, 1, 2, 2}, new \r
+                       int[]{1, 4, 1, 2, 2, 1}, new int[]{1, 1, 2, 2, 1, 4}, new int[]{1, 1, 2, 4, 1, 2}, new int[]{1, 2, 2, 1, 1, 4}, new int[]{1, 2, 2, 4, 1, 1}, new int[]{1, 4, 2, 1, 1, 2}, new int[]{1, 4, 2, 2, 1, 1}, new int[]{2, 4, 1, 2, 1, 1}, new int[]{2, 2, 1, 1, 1, 4}, new int[]{4, 1, 3, 1, 1, 1}, new int[]{2, 4, 1, 1, 1, 2}, new int[]{1, 3, 4, 1, 1, 1}, new int[]{1, 1, 1, 2, 4, 2}, new int[]{1, 2, 1, 1, 4, 2}, new int[]{1, 2, 1, 2, 4, 1}, new int[]{1, 1, 4, 2, 1, 2}, new int[]{1, 2, 4, 1, 1, 2}, new int[]{1, 2, 4, 2, 1, 1}, new int[]{4, 1, 1, 2, 1, 2}, new int[]{4, 2, 1, 1, 1, 2}, new int[]{4, 2, 1, 2, 1, 1}, new int[]{2, 1, 2, 1, 4, 1}, new int[]{2, 1, 4, 1, 2, 1}, new int[]{4, 1, 2, 1, 2, 1}, new int[]{1, 1, 1, 1, 4, 3}, new int[]{1, 1, 1, 3, 4, 1}, new int[]{1, 3, 1, 1, 4, 1}, new int[]{1, 1, 4, 1, 1, 3}, new int[]{1, 1, 4, 3, 1, 1}, new int[]{4, 1, 1, 1, 1, 3}, new int[]{4, 1, 1, 3, 1, 1}, new int[]{1, 1, 3, 1, 4, 1}, new int[]{1, 1, 4, 1, 3, 1}, new int[]{3, 1, 1, 1, 4, 1}, new int[]{4, 1, 1, 1, 3, 1}, new int[]{2, 1, 1, 4, 1, 2}, new int[]{2, 1, 1, 2, 1, 4}, new int[]{2, 1, 1, 2, 3, 2}, new int[]{2, 3, 3, 1, 1, 1, 2}};\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.25f);\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.7f);\r
+               \r
+               private const int CODE_SHIFT = 98;\r
+               \r
+               private const int CODE_CODE_C = 99;\r
+               private const int CODE_CODE_B = 100;\r
+               private const int CODE_CODE_A = 101;\r
+               \r
+               private const int CODE_FNC_1 = 102;\r
+               private const int CODE_FNC_2 = 97;\r
+               private const int CODE_FNC_3 = 96;\r
+               private const int CODE_FNC_4_A = 101;\r
+               private const int CODE_FNC_4_B = 100;\r
+               \r
+               private const int CODE_START_A = 103;\r
+               private const int CODE_START_B = 104;\r
+               private const int CODE_START_C = 105;\r
+               private const int CODE_STOP = 106;\r
+               \r
+               private static int[] findStartPattern(BitArray row)\r
+               {\r
+                       int width = row.Size;\r
+                       int rowOffset = 0;\r
+                       while (rowOffset < width)\r
+                       {\r
+                               if (row.get_Renamed(rowOffset))\r
+                               {\r
+                                       break;\r
+                               }\r
+                               rowOffset++;\r
+                       }\r
+                       \r
+                       int counterPosition = 0;\r
+                       int[] counters = new int[6];\r
+                       int patternStart = rowOffset;\r
+                       bool isWhite = false;\r
+                       int patternLength = counters.Length;\r
+                       \r
+                       for (int i = rowOffset; i < width; i++)\r
+                       {\r
+                               bool pixel = row.get_Renamed(i);\r
+                               if (pixel ^ isWhite)\r
+                               {\r
+                                       counters[counterPosition]++;\r
+                               }\r
+                               else\r
+                               {\r
+                                       if (counterPosition == patternLength - 1)\r
+                                       {\r
+                                               int bestVariance = MAX_AVG_VARIANCE;\r
+                                               int bestMatch = - 1;\r
+                                               for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++)\r
+                                               {\r
+                                                       int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE);\r
+                                                       if (variance < bestVariance)\r
+                                                       {\r
+                                                               bestVariance = variance;\r
+                                                               bestMatch = startCode;\r
+                                                       }\r
+                                               }\r
+                                               if (bestMatch >= 0)\r
+                                               {\r
+                                                       // Look for whitespace before start pattern, >= 50% of width of start pattern\r
+                                                       if (row.isRange(System.Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false))\r
+                                                       {\r
+                                                               return new int[]{patternStart, i, bestMatch};\r
+                                                       }\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
+               private static int decodeCode(BitArray row, int[] counters, int rowOffset)\r
+               {\r
+                       recordPattern(row, rowOffset, counters);\r
+                       int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept\r
+                       int bestMatch = - 1;\r
+                       for (int d = 0; d < CODE_PATTERNS.Length; d++)\r
+                       {\r
+                               int[] pattern = CODE_PATTERNS[d];\r
+                               int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);\r
+                               if (variance < bestVariance)\r
+                               {\r
+                                       bestVariance = variance;\r
+                                       bestMatch = d;\r
+                               }\r
+                       }\r
+                       // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6.\r
+                       if (bestMatch >= 0)\r
+                       {\r
+                               return bestMatch;\r
+                       }\r
+                       else\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+               }\r
+               \r
+               public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints)\r
+               {\r
+                       \r
+                       int[] startPatternInfo = findStartPattern(row);\r
+                       int startCode = startPatternInfo[2];\r
+                       int codeSet;\r
+                       switch (startCode)\r
+                       {\r
+                               \r
+                               case CODE_START_A: \r
+                                       codeSet = CODE_CODE_A;\r
+                                       break;\r
+                               \r
+                               case CODE_START_B: \r
+                                       codeSet = CODE_CODE_B;\r
+                                       break;\r
+                               \r
+                               case CODE_START_C: \r
+                                       codeSet = CODE_CODE_C;\r
+                                       break;\r
+                               \r
+                               default: \r
+                                       throw ReaderException.Instance;\r
+                               \r
+                       }\r
+                       \r
+                       bool done = false;\r
+                       bool isNextShifted = false;\r
+                       \r
+                       System.Text.StringBuilder result = new System.Text.StringBuilder(20);\r
+                       int lastStart = startPatternInfo[0];\r
+                       int nextStart = startPatternInfo[1];\r
+                       int[] counters = new int[6];\r
+                       \r
+                       int lastCode = 0;\r
+                       int code = 0;\r
+                       int checksumTotal = startCode;\r
+                       int multiplier = 0;\r
+                       bool lastCharacterWasPrintable = true;\r
+                       \r
+                       while (!done)\r
+                       {\r
+                               \r
+                               bool unshift = isNextShifted;\r
+                               isNextShifted = false;\r
+                               \r
+                               // Save off last code\r
+                               lastCode = code;\r
+                               \r
+                               // Decode another code from image\r
+                               code = decodeCode(row, counters, nextStart);\r
+                               \r
+                               // Remember whether the last code was printable or not (excluding CODE_STOP)\r
+                               if (code != CODE_STOP)\r
+                               {\r
+                                       lastCharacterWasPrintable = true;\r
+                               }\r
+                               \r
+                               // Add to checksum computation (if not CODE_STOP of course)\r
+                               if (code != CODE_STOP)\r
+                               {\r
+                                       multiplier++;\r
+                                       checksumTotal += multiplier * code;\r
+                               }\r
+                               \r
+                               // Advance to where the next code will to start\r
+                               lastStart = nextStart;\r
+                               for (int i = 0; i < counters.Length; i++)\r
+                               {\r
+                                       nextStart += counters[i];\r
+                               }\r
+                               \r
+                               // Take care of illegal start codes\r
+                               switch (code)\r
+                               {\r
+                                       \r
+                                       case CODE_START_A: \r
+                                       case CODE_START_B: \r
+                                       case CODE_START_C: \r
+                                               throw ReaderException.Instance;\r
+                                       }\r
+                               \r
+                               switch (codeSet)\r
+                               {\r
+                                       \r
+                                       \r
+                                       case CODE_CODE_A: \r
+                                               if (code < 64)\r
+                                               {\r
+                                                       result.Append((char) (' ' + code));\r
+                                               }\r
+                                               else if (code < 96)\r
+                                               {\r
+                                                       result.Append((char) (code - 64));\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       // Don't let CODE_STOP, which always appears, affect whether whether we think the last\r
+                                                       // code was printable or not.\r
+                                                       if (code != CODE_STOP)\r
+                                                       {\r
+                                                               lastCharacterWasPrintable = false;\r
+                                                       }\r
+                                                       switch (code)\r
+                                                       {\r
+                                                               \r
+                                                               case CODE_FNC_1: \r
+                                                               case CODE_FNC_2: \r
+                                                               case CODE_FNC_3: \r
+                                                               case CODE_FNC_4_A: \r
+                                                                       // do nothing?\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_SHIFT: \r
+                                                                       isNextShifted = true;\r
+                                                                       codeSet = CODE_CODE_B;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_B: \r
+                                                                       codeSet = CODE_CODE_B;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_C: \r
+                                                                       codeSet = CODE_CODE_C;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_STOP: \r
+                                                                       done = true;\r
+                                                                       break;\r
+                                                               }\r
+                                               }\r
+                                               break;\r
+                                       \r
+                                       case CODE_CODE_B: \r
+                                               if (code < 96)\r
+                                               {\r
+                                                       result.Append((char) (' ' + code));\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       if (code != CODE_STOP)\r
+                                                       {\r
+                                                               lastCharacterWasPrintable = false;\r
+                                                       }\r
+                                                       switch (code)\r
+                                                       {\r
+                                                               \r
+                                                               case CODE_FNC_1: \r
+                                                               case CODE_FNC_2: \r
+                                                               case CODE_FNC_3: \r
+                                                               case CODE_FNC_4_B: \r
+                                                                       // do nothing?\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_SHIFT: \r
+                                                                       isNextShifted = true;\r
+                                                                       codeSet = CODE_CODE_C;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_A: \r
+                                                                       codeSet = CODE_CODE_A;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_C: \r
+                                                                       codeSet = CODE_CODE_C;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_STOP: \r
+                                                                       done = true;\r
+                                                                       break;\r
+                                                               }\r
+                                               }\r
+                                               break;\r
+                                       \r
+                                       case CODE_CODE_C: \r
+                                               if (code < 100)\r
+                                               {\r
+                                                       if (code < 10)\r
+                                                       {\r
+                                                               result.Append('0');\r
+                                                       }\r
+                                                       result.Append(code);\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       if (code != CODE_STOP)\r
+                                                       {\r
+                                                               lastCharacterWasPrintable = false;\r
+                                                       }\r
+                                                       switch (code)\r
+                                                       {\r
+                                                               \r
+                                                               case CODE_FNC_1: \r
+                                                                       // do nothing?\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_A: \r
+                                                                       codeSet = CODE_CODE_A;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_CODE_B: \r
+                                                                       codeSet = CODE_CODE_B;\r
+                                                                       break;\r
+                                                               \r
+                                                               case CODE_STOP: \r
+                                                                       done = true;\r
+                                                                       break;\r
+                                                               }\r
+                                               }\r
+                                               break;\r
+                                       }\r
+                               \r
+                               // Unshift back to another code set if we were shifted\r
+                               if (unshift)\r
+                               {\r
+                                       switch (codeSet)\r
+                                       {\r
+                                               \r
+                                               case CODE_CODE_A: \r
+                                                       codeSet = CODE_CODE_C;\r
+                                                       break;\r
+                                               \r
+                                               case CODE_CODE_B: \r
+                                                       codeSet = CODE_CODE_A;\r
+                                                       break;\r
+                                               \r
+                                               case CODE_CODE_C: \r
+                                                       codeSet = CODE_CODE_B;\r
+                                                       break;\r
+                                               }\r
+                               }\r
+                       }\r
+                       \r
+                       // Check for ample whitespace following pattern, but, to do this we first need to remember that\r
+                       // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left\r
+                       // to read off. Would be slightly better to properly read. Here we just skip it:\r
+                       int width = row.Size;\r
+                       while (nextStart < width && row.get_Renamed(nextStart))\r
+                       {\r
+                               nextStart++;\r
+                       }\r
+                       if (!row.isRange(nextStart, System.Math.Min(width, nextStart + (nextStart - lastStart) / 2), false))\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+                       \r
+                       // Pull out from sum the value of the penultimate check code\r
+                       checksumTotal -= multiplier * lastCode;\r
+                       // lastCode is the checksum then:\r
+                       if (checksumTotal % 103 != lastCode)\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+                       \r
+                       // Need to pull out the check digits from string\r
+                       int resultLength = result.Length;\r
+                       // Only bother if the result had at least one character, and if the checksum digit happened to\r
+                       // be a printable character. If it was just interpreted as a control code, nothing to remove.\r
+                       if (resultLength > 0 && lastCharacterWasPrintable)\r
+                       {\r
+                               if (codeSet == CODE_CODE_C)\r
+                               {\r
+                                       result.Remove(resultLength - 2, resultLength - (resultLength - 2));\r
+                               }\r
+                               else\r
+                               {\r
+                                       result.Remove(resultLength - 1, resultLength - (resultLength - 1));\r
+                               }\r
+                       }\r
+                       \r
+                       System.String resultString = result.ToString();\r
+                       \r
+                       if (resultString.Length == 0)\r
+                       {\r
+                               // Almost surely a false positive\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
+                       float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;\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
+                       float right = (float) (nextStart + lastStart) / 2.0f;\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(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_128);\r
+               }\r
+       }\r
 }
\ No newline at end of file