/// <summary> Records the size of successive runs of white and black pixels in a row, starting at a given point. /// The values are recorded in the given array, and the number of runs recorded is equal to the size /// of the array. If the row starts on a white pixel at the given start point, then the first count /// recorded is the run of white pixels starting from that point; likewise it is the count of a run /// of black pixels if the row begin on a black pixels at that point. /// /// </summary> /// <param name="row">row to count from /// </param> /// <param name="start">offset into row to start at /// </param> /// <param name="counters">array into which to record counts /// </param> /// <throws> ReaderException if counters cannot be filled entirely from row before running out </throws> /// <summary> of pixels /// </summary> internal static void recordPattern(BitArray row, int start, int[] counters) { int numCounters = counters.Length; for (int i = 0; i < numCounters; i++) { counters[i] = 0; } int end = row.Size; if (start >= end) { throw ReaderException.Instance; } bool isWhite = !row.get_Renamed(start); int counterPosition = 0; int i2 = start; while (i2 < end) { bool pixel = row.get_Renamed(i2); if (pixel ^ isWhite) { // that is, exactly one is true counters[counterPosition]++; } else { counterPosition++; if (counterPosition == numCounters) { break; } else { counters[counterPosition] = 1; isWhite ^= true; // isWhite = !isWhite; } } i2++; } // If we read fully the last section of pixels and filled up our counters -- or filled // the last counter but ran off the side of the image, OK. Otherwise, a problem. if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i2 == end))) { throw ReaderException.Instance; } }
/// <summary> <p>Attempts to decode a one-dimensional barcode format given a single row of /// an image.</p> /// /// </summary> /// <param name="rowNumber">row number from top of the row /// </param> /// <param name="row">the black/white pixel data of the row /// </param> /// <param name="hints">decode hints /// </param> /// <returns> {@link Result} containing encoded string and start/end of barcode /// </returns> /// <throws> ReaderException if an error occurs or barcode cannot be found </throws> public abstract Result decodeRow(int rowNumber, BitArray row, Dictionary<DecodeHintType, Object> hints);
/// <summary> We're going to examine rows from the middle outward, searching alternately above and below the /// middle, and farther out each time. rowStep is the number of rows between each successive /// attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then /// middle + rowStep, then middle - (2 * rowStep), etc. /// rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily /// decided that moving up and down by about 1/16 of the image is pretty good; we try more of the /// image if "trying harder". /// /// </summary> /// <param name="image">The image to decode /// </param> /// <param name="hints">Any hints that were requested /// </param> /// <returns> The contents of the decoded barcode /// </returns> /// <throws> ReaderException Any spontaneous errors which occur </throws> private Result doDecode(BinaryBitmap image, Dictionary<DecodeHintType, Object> hints) { int width = image.Width; int height = image.Height; var row = new BitArray(width); int middle = height >> 1; bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); int rowStep = Math.Max(1, height >> (tryHarder ? 7 : 4)); int maxLines; if (tryHarder) { maxLines = height; // Look at the whole image, not just the center } else { maxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image } for (int x = 0; x < maxLines; x++) { // Scanning from the middle out. Determine which row we're looking at next: int rowStepsAboveOrBelow = (x + 1) >> 1; bool isAbove = (x & 0x01) == 0; // i.e. is x even? int rowNumber = middle + rowStep*(isAbove ? rowStepsAboveOrBelow : - rowStepsAboveOrBelow); if (rowNumber < 0 || rowNumber >= height) { // Oops, if we run off the top or bottom, stop break; } // Estimate black point for this row and load it: try { row = image.getBlackRow(rowNumber, row); } catch (ReaderException) { continue; } // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to // handle decoding upside down barcodes. for (int attempt = 0; attempt < 2; attempt++) { if (attempt == 1) { // trying again? row.reverse(); // reverse the row and continue // This means we will only ever draw result points *once* in the life of this method // since we want to avoid drawing the wrong points after flipping the row, and, // don't want to clutter with noise from every single row scan -- just the scans // that start on the center line. if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) { var newHints = new Dictionary<DecodeHintType, Object>(); // Can't use clone() in J2ME foreach (var key in hints.Keys.Where(key => !key.Equals(DecodeHintType.NEED_RESULT_POINT_CALLBACK))) { newHints[key] = hints[key]; } hints = newHints; } } try { // Look for a barcode Result result = decodeRow(rowNumber, row, hints); // We found our barcode if (attempt == 1) { // But it was upside down, so note that result.putMetadata(ResultMetadataType.ORIENTATION, 180); // And remember to flip the result points horizontally. ResultPoint[] points = result.ResultPoints; points[0] = new ResultPoint(width - points[0].X - 1, points[0].Y); points[1] = new ResultPoint(width - points[1].X - 1, points[1].Y); } return result; } catch (ReaderException) { // continue -- just couldn't decode this row } } } throw ReaderException.Instance; }