/* * Copyright 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // package com::google::zxing::oned; /** * Encapsulates functionality and implementation that is common to all families * of one-dimensional barcodes. * * @author dswitkin@google.com (Daniel Switkin) * @author Sean Owen */ #[derive(Reader)] pub struct OneDReader { } impl OneDReader { pub fn decode(&self, image: &BinaryBitmap) -> /* throws NotFoundException, FormatException */Result> { return Ok(self.decode(image, null)); } // Note that we don't try rotation without the try harder flag, even if rotation was supported. pub fn decode(&self, image: &BinaryBitmap, hints: &Map) -> /* throws NotFoundException, FormatException */Result> { let tryResult1 = 0; 'try1: loop { { return Ok(self.do_decode(image, &hints)); } break 'try1 } match tryResult1 { catch ( nfe: &NotFoundException) { let try_harder: bool = hints != null && hints.contains_key(DecodeHintType::TRY_HARDER); if try_harder && image.is_rotate_supported() { let rotated_image: BinaryBitmap = image.rotate_counter_clockwise(); let result: Result = self.do_decode(rotated_image, &hints); let metadata: Map = result.get_result_metadata(); let mut orientation: i32 = 270; if metadata != null && metadata.contains_key(ResultMetadataType::ORIENTATION) { orientation = (orientation + metadata.get(ResultMetadataType::ORIENTATION) as Integer) % 360; } result.put_metadata(ResultMetadataType::ORIENTATION, orientation); let mut points: Vec = result.get_result_points(); if points != null { let height: i32 = rotated_image.get_height(); { let mut i: i32 = 0; while i < points.len() { { points[i] = ResultPoint::new(height - points[i].get_y() - 1, &points[i].get_x()); } i += 1; } } } return Ok(result); } else { throw nfe; } } 0 => break } } pub fn reset(&self) { // do nothing } /** * 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". * * @param image The image to decode * @param hints Any hints that were requested * @return The contents of the decoded barcode * @throws NotFoundException Any spontaneous errors which occur */ fn do_decode(&self, image: &BinaryBitmap, hints: &Map) -> /* throws NotFoundException */Result> { let width: i32 = image.get_width(); let height: i32 = image.get_height(); let mut row: BitArray = BitArray::new(width); let try_harder: bool = hints != null && hints.contains_key(DecodeHintType::TRY_HARDER); let row_step: i32 = Math::max(1, height >> ( if try_harder { 8 } else { 5 })); let max_lines: i32; if try_harder { // Look at the whole image, not just the center max_lines = height; } else { // 15 rows spaced 1/32 apart is roughly the middle half of the image max_lines = 15; } let middle: i32 = height / 2; { let mut x: i32 = 0; while x < max_lines { { // Scanning from the middle out. Determine which row we're looking at next: let row_steps_above_or_below: i32 = (x + 1) / 2; // i.e. is x even? let is_above: bool = (x & 0x01) == 0; let row_number: i32 = middle + row_step * ( if is_above { row_steps_above_or_below } else { -row_steps_above_or_below }); if row_number < 0 || row_number >= height { // Oops, if we run off the top or bottom, stop break; } // Estimate black point for this row and load it: let tryResult1 = 0; 'try1: loop { { row = image.get_black_row(row_number, row); } break 'try1 } match tryResult1 { catch ( ignored: &NotFoundException) { continue; } 0 => break } // handle decoding upside down barcodes. { let mut attempt: i32 = 0; while attempt < 2 { { if attempt == 1 { // trying again? // reverse the row and continue row.reverse(); // that start on the center line. if hints != null && hints.contains_key(DecodeHintType::NEED_RESULT_POINT_CALLBACK) { let new_hints: Map = EnumMap<>::new(DecodeHintType.class); new_hints.put_all(&hints); new_hints.remove(DecodeHintType::NEED_RESULT_POINT_CALLBACK); hints = new_hints; } } let tryResult1 = 0; 'try1: loop { { // Look for a barcode let result: Result = self.decode_row(row_number, row, &hints); // We found our barcode if attempt == 1 { // But it was upside down, so note that result.put_metadata(ResultMetadataType::ORIENTATION, 180); // And remember to flip the result points horizontally. let mut points: Vec = result.get_result_points(); if points != null { points[0] = ResultPoint::new(width - points[0].get_x() - 1, &points[0].get_y()); points[1] = ResultPoint::new(width - points[1].get_x() - 1, &points[1].get_y()); } } return Ok(result); } break 'try1 } match tryResult1 { catch ( re: &ReaderException) { } 0 => break } } attempt += 1; } } } x += 1; } } throw NotFoundException::get_not_found_instance(); } /** * 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. * * @param row row to count from * @param start offset into row to start at * @param counters array into which to record counts * @throws NotFoundException if counters cannot be filled entirely from row before running out * of pixels */ pub fn record_pattern( row: &BitArray, start: i32, counters: &Vec) -> /* throws NotFoundException */Result> { let num_counters: i32 = counters.len(); Arrays::fill(&counters, 0, num_counters, 0); let end: i32 = row.get_size(); if start >= end { throw NotFoundException::get_not_found_instance(); } let is_white: bool = !row.get(start); let counter_position: i32 = 0; let mut i: i32 = start; while i < end { if row.get(i) != is_white { counters[counter_position] += 1; } else { if counter_position += 1 == num_counters { break; } else { counters[counter_position] = 1; is_white = !is_white; } } i += 1; } // the last counter but ran off the side of the image, OK. Otherwise, a problem. if !(counter_position == num_counters || (counter_position == num_counters - 1 && i == end)) { throw NotFoundException::get_not_found_instance(); } } pub fn record_pattern_in_reverse( row: &BitArray, start: i32, counters: &Vec) -> /* throws NotFoundException */Result> { // This could be more efficient I guess let num_transitions_left: i32 = counters.len(); let mut last: bool = row.get(start); while start > 0 && num_transitions_left >= 0 { if row.get(start -= 1) != last { num_transitions_left -= 1; last = !last; } } if num_transitions_left >= 0 { throw NotFoundException::get_not_found_instance(); } ::record_pattern(row, start + 1, &counters); } /** * Determines how closely a set of observed counts of runs of black/white values matches a given * target pattern. This is reported as the ratio of the total variance from the expected pattern * proportions across all pattern elements, to the length of the pattern. * * @param counters observed counters * @param pattern expected pattern * @param maxIndividualVariance The most any counter can differ before we give up * @return ratio of total variance between counters and pattern compared to total pattern size */ pub fn pattern_match_variance( counters: &Vec, pattern: &Vec, max_individual_variance: f32) -> f32 { let num_counters: i32 = counters.len(); let mut total: i32 = 0; let pattern_length: i32 = 0; { let mut i: i32 = 0; while i < num_counters { { total += counters[i]; pattern_length += pattern[i]; } i += 1; } } if total < pattern_length { // to reliably match, so fail: return Float::POSITIVE_INFINITY; } let unit_bar_width: f32 = total as f32 / pattern_length; max_individual_variance *= unit_bar_width; let total_variance: f32 = 0.0f; { let mut x: i32 = 0; while x < num_counters { { let counter: i32 = counters[x]; let scaled_pattern: f32 = pattern[x] * unit_bar_width; let variance: f32 = if counter > scaled_pattern { counter - scaled_pattern } else { scaled_pattern - counter }; if variance > max_individual_variance { return Float::POSITIVE_INFINITY; } total_variance += variance; } x += 1; } } return total_variance / total; } /** *

Attempts to decode a one-dimensional barcode format given a single row of * an image.

* * @param rowNumber row number from top of the row * @param row the black/white pixel data of the row * @param hints decode hints * @return {@link Result} containing encoded string and start/end of barcode * @throws NotFoundException if no potential barcode is found * @throws ChecksumException if a potential barcode is found but does not pass its checksum * @throws FormatException if a potential barcode is found but format is invalid */ pub fn decode_row(&self, row_number: i32, row: &BitArray, hints: &Map) -> /* throws NotFoundException, ChecksumException, FormatException */Result> ; }