New C# port from Suraj Supekar
[zxing.git] / csharp / oned / OneDReader.cs
diff --git a/csharp/oned/OneDReader.cs b/csharp/oned/OneDReader.cs
new file mode 100755 (executable)
index 0000000..bdb768b
--- /dev/null
@@ -0,0 +1,332 @@
+/*\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
+*\r
+*      http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*/\r
+using System;\r
+using BinaryBitmap = com.google.zxing.BinaryBitmap;\r
+using DecodeHintType = com.google.zxing.DecodeHintType;\r
+using Reader = com.google.zxing.Reader;\r
+using ReaderException = com.google.zxing.ReaderException;\r
+using Result = com.google.zxing.Result;\r
+using ResultMetadataType = com.google.zxing.ResultMetadataType;\r
+using ResultPoint = com.google.zxing.ResultPoint;\r
+using BitArray = com.google.zxing.common.BitArray;\r
+namespace com.google.zxing.oned\r
+{\r
+       \r
+       /// <summary> Encapsulates functionality and implementation that is common to all families\r
+       /// of one-dimensional barcodes.\r
+       /// \r
+       /// </summary>\r
+       /// <author>  dswitkin@google.com (Daniel Switkin)\r
+       /// </author>\r
+       /// <author>  Sean Owen\r
+       /// </author>\r
+       /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
+       /// </author>\r
+       public abstract class OneDReader : Reader\r
+       {\r
+               \r
+               private const int INTEGER_MATH_SHIFT = 8;\r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'PATTERN_MATCH_RESULT_SCALE_FACTOR '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               internal static readonly int PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT;\r
+               \r
+               public virtual Result decode(BinaryBitmap image)\r
+               {\r
+                       return decode(image, null);\r
+               }\r
+               \r
+               // Note that we don't try rotation without the try harder flag, even if rotation was supported.\r
+               public virtual Result decode(BinaryBitmap image, System.Collections.Hashtable hints)\r
+               {\r
+                       try\r
+                       {\r
+                               return doDecode(image, hints);\r
+                       }\r
+                       catch (ReaderException re)\r
+                       {\r
+                               bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER);\r
+                               if (tryHarder && image.RotateSupported)\r
+                               {\r
+                                       BinaryBitmap rotatedImage = image.rotateCounterClockwise();\r
+                                       Result result = doDecode(rotatedImage, hints);\r
+                                       // Record that we found it rotated 90 degrees CCW / 270 degrees CW\r
+                                       System.Collections.Hashtable metadata = result.ResultMetadata;\r
+                                       int orientation = 270;\r
+                                       if (metadata != null && metadata.ContainsKey(ResultMetadataType.ORIENTATION))\r
+                                       {\r
+                                               // But if we found it reversed in doDecode(), add in that result here:\r
+                                               orientation = (orientation + ((System.Int32) metadata[ResultMetadataType.ORIENTATION])) % 360;\r
+                                       }\r
+                                       result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) orientation);\r
+                                       // Update result points\r
+                                       ResultPoint[] points = result.ResultPoints;\r
+                                       int height = rotatedImage.Height;\r
+                                       for (int i = 0; i < points.Length; i++)\r
+                                       {\r
+                                               points[i] = new ResultPoint(height - points[i].Y - 1, points[i].X);\r
+                                       }\r
+                                       return result;\r
+                               }\r
+                               else\r
+                               {\r
+                                       throw re;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               /// <summary> We're going to examine rows from the middle outward, searching alternately above and below the\r
+               /// middle, and farther out each time. rowStep is the number of rows between each successive\r
+               /// attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then\r
+               /// middle + rowStep, then middle - (2 * rowStep), etc.\r
+               /// rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily\r
+               /// decided that moving up and down by about 1/16 of the image is pretty good; we try more of the\r
+               /// image if "trying harder".\r
+               /// \r
+               /// </summary>\r
+               /// <param name="image">The image to decode\r
+               /// </param>\r
+               /// <param name="hints">Any hints that were requested\r
+               /// </param>\r
+               /// <returns> The contents of the decoded barcode\r
+               /// </returns>\r
+               /// <throws>  ReaderException Any spontaneous errors which occur </throws>\r
+               private Result doDecode(BinaryBitmap image, System.Collections.Hashtable hints)\r
+               {\r
+                       int width = image.Width;\r
+                       int height = image.Height;\r
+                       BitArray row = new BitArray(width);\r
+                       \r
+                       int middle = height >> 1;\r
+                       bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER);\r
+                       int rowStep = System.Math.Max(1, height >> (tryHarder?7:4));\r
+                       int maxLines;\r
+                       if (tryHarder)\r
+                       {\r
+                               maxLines = height; // Look at the whole image, not just the center\r
+                       }\r
+                       else\r
+                       {\r
+                               maxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image\r
+                       }\r
+                       \r
+                       for (int x = 0; x < maxLines; x++)\r
+                       {\r
+                               \r
+                               // Scanning from the middle out. Determine which row we're looking at next:\r
+                               int rowStepsAboveOrBelow = (x + 1) >> 1;\r
+                               bool isAbove = (x & 0x01) == 0; // i.e. is x even?\r
+                               int rowNumber = middle + rowStep * (isAbove?rowStepsAboveOrBelow:- rowStepsAboveOrBelow);\r
+                               if (rowNumber < 0 || rowNumber >= height)\r
+                               {\r
+                                       // Oops, if we run off the top or bottom, stop\r
+                                       break;\r
+                               }\r
+                               \r
+                               // Estimate black point for this row and load it:\r
+                               try\r
+                               {\r
+                                       row = image.getBlackRow(rowNumber, row);\r
+                               }\r
+                               catch (ReaderException re)\r
+                               {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to\r
+                               // handle decoding upside down barcodes.\r
+                               for (int attempt = 0; attempt < 2; attempt++)\r
+                               {\r
+                                       if (attempt == 1)\r
+                                       {\r
+                                               // trying again?\r
+                                               row.reverse(); // reverse the row and continue\r
+                                               // This means we will only ever draw result points *once* in the life of this method\r
+                                               // since we want to avoid drawing the wrong points after flipping the row, and,\r
+                                               // don't want to clutter with noise from every single row scan -- just the scans\r
+                                               // that start on the center line.\r
+                                               if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK))\r
+                                               {\r
+                                                       System.Collections.Hashtable newHints = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); // Can't use clone() in J2ME\r
+                                                       System.Collections.IEnumerator hintEnum = hints.Keys.GetEnumerator();\r
+                                                       //UPGRADE_TODO: Method 'java.util.Enumeration.hasMoreElements' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationhasMoreElements'"\r
+                                                       while (hintEnum.MoveNext())\r
+                                                       {\r
+                                                               //UPGRADE_TODO: Method 'java.util.Enumeration.nextElement' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationnextElement'"\r
+                                                               System.Object key = hintEnum.Current;\r
+                                                               if (!key.Equals(DecodeHintType.NEED_RESULT_POINT_CALLBACK))\r
+                                                               {\r
+                                                                       newHints[key] = hints[key];\r
+                                                               }\r
+                                                       }\r
+                                                       hints = newHints;\r
+                                               }\r
+                                       }\r
+                                       try\r
+                                       {\r
+                                               // Look for a barcode\r
+                                               Result result = decodeRow(rowNumber, row, hints);\r
+                                               // We found our barcode\r
+                                               if (attempt == 1)\r
+                                               {\r
+                                                       // But it was upside down, so note that\r
+                                                       result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) 180);\r
+                                                       // And remember to flip the result points horizontally.\r
+                                                       ResultPoint[] points = result.ResultPoints;\r
+                                                       points[0] = new ResultPoint(width - points[0].X - 1, points[0].Y);\r
+                                                       points[1] = new ResultPoint(width - points[1].X - 1, points[1].Y);\r
+                                               }\r
+                                               return result;\r
+                                       }\r
+                                       catch (ReaderException re)\r
+                                       {\r
+                                               // continue -- just couldn't decode this row\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       throw ReaderException.Instance;\r
+               }\r
+               \r
+               /// <summary> Records the size of successive runs of white and black pixels in a row, starting at a given point.\r
+               /// The values are recorded in the given array, and the number of runs recorded is equal to the size\r
+               /// of the array. If the row starts on a white pixel at the given start point, then the first count\r
+               /// recorded is the run of white pixels starting from that point; likewise it is the count of a run\r
+               /// of black pixels if the row begin on a black pixels at that point.\r
+               /// \r
+               /// </summary>\r
+               /// <param name="row">row to count from\r
+               /// </param>\r
+               /// <param name="start">offset into row to start at\r
+               /// </param>\r
+               /// <param name="counters">array into which to record counts\r
+               /// </param>\r
+               /// <throws>  ReaderException if counters cannot be filled entirely from row before running out </throws>\r
+               /// <summary>  of pixels\r
+               /// </summary>\r
+               internal static void  recordPattern(BitArray row, int start, int[] counters)\r
+               {\r
+                       int numCounters = counters.Length;\r
+                       for (int i = 0; i < numCounters; i++)\r
+                       {\r
+                               counters[i] = 0;\r
+                       }\r
+                       int end = row.Size;\r
+                       if (start >= end)\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+                       bool isWhite = !row.get_Renamed(start);\r
+                       int counterPosition = 0;\r
+                       int i2 = start;\r
+                       while (i2 < end)\r
+                       {\r
+                               bool pixel = row.get_Renamed(i2);\r
+                               if (pixel ^ isWhite)\r
+                               {\r
+                                       // that is, exactly one is true\r
+                                       counters[counterPosition]++;\r
+                               }\r
+                               else\r
+                               {\r
+                                       counterPosition++;\r
+                                       if (counterPosition == numCounters)\r
+                                       {\r
+                                               break;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               counters[counterPosition] = 1;\r
+                                               isWhite ^= true; // isWhite = !isWhite;\r
+                                       }\r
+                               }\r
+                               i2++;\r
+                       }\r
+                       // If we read fully the last section of pixels and filled up our counters -- or filled\r
+                       // the last counter but ran off the side of the image, OK. Otherwise, a problem.\r
+                       if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i2 == end)))\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+               }\r
+               \r
+               /// <summary> Determines how closely a set of observed counts of runs of black/white values matches a given\r
+               /// target pattern. This is reported as the ratio of the total variance from the expected pattern\r
+               /// proportions across all pattern elements, to the length of the pattern.\r
+               /// \r
+               /// </summary>\r
+               /// <param name="counters">observed counters\r
+               /// </param>\r
+               /// <param name="pattern">expected pattern\r
+               /// </param>\r
+               /// <param name="maxIndividualVariance">The most any counter can differ before we give up\r
+               /// </param>\r
+               /// <returns> ratio of total variance between counters and pattern compared to total pattern size,\r
+               /// where the ratio has been multiplied by 256. So, 0 means no variance (perfect match); 256 means\r
+               /// the total variance between counters and patterns equals the pattern length, higher values mean\r
+               /// even more variance\r
+               /// </returns>\r
+               internal static int patternMatchVariance(int[] counters, int[] pattern, int maxIndividualVariance)\r
+               {\r
+                       int numCounters = counters.Length;\r
+                       int total = 0;\r
+                       int patternLength = 0;\r
+                       for (int i = 0; i < numCounters; i++)\r
+                       {\r
+                               total += counters[i];\r
+                               patternLength += pattern[i];\r
+                       }\r
+                       if (total < patternLength)\r
+                       {\r
+                               // If we don't even have one pixel per unit of bar width, assume this is too small\r
+                               // to reliably match, so fail:\r
+                               return System.Int32.MaxValue;\r
+                       }\r
+                       // We're going to fake floating-point math in integers. We just need to use more bits.\r
+                       // Scale up patternLength so that intermediate values below like scaledCounter will have\r
+                       // more "significant digits"\r
+                       int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;\r
+                       maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;\r
+                       \r
+                       int totalVariance = 0;\r
+                       for (int x = 0; x < numCounters; x++)\r
+                       {\r
+                               int counter = counters[x] << INTEGER_MATH_SHIFT;\r
+                               int scaledPattern = pattern[x] * unitBarWidth;\r
+                               int variance = counter > scaledPattern?counter - scaledPattern:scaledPattern - counter;\r
+                               if (variance > maxIndividualVariance)\r
+                               {\r
+                                       return System.Int32.MaxValue;\r
+                               }\r
+                               totalVariance += variance;\r
+                       }\r
+                       return totalVariance / total;\r
+               }\r
+               \r
+               /// <summary> <p>Attempts to decode a one-dimensional barcode format given a single row of\r
+               /// an image.</p>\r
+               /// \r
+               /// </summary>\r
+               /// <param name="rowNumber">row number from top of the row\r
+               /// </param>\r
+               /// <param name="row">the black/white pixel data of the row\r
+               /// </param>\r
+               /// <param name="hints">decode hints\r
+               /// </param>\r
+               /// <returns> {@link Result} containing encoded string and start/end of barcode\r
+               /// </returns>\r
+               /// <throws>  ReaderException if an error occurs or barcode cannot be found </throws>\r
+               public abstract Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints);\r
+       }\r
+}
\ No newline at end of file