Files
rxing/port_src/output/zxing/oned/one_d_reader.rs
2022-08-12 16:58:30 -05:00

318 lines
14 KiB
Rust

/*
* 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<Result, Rc<Exception>> {
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<DecodeHintType, ?>) -> /* throws NotFoundException, FormatException */Result<Result, Rc<Exception>> {
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<ResultMetadataType, ?> = 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<ResultPoint> = 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<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Result, Rc<Exception>> {
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<DecodeHintType, Object> = 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<ResultPoint> = 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<i32>) -> /* throws NotFoundException */Result<Void, Rc<Exception>> {
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<i32>) -> /* throws NotFoundException */Result<Void, Rc<Exception>> {
// 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<i32>, pattern: &Vec<i32>, 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;
}
/**
* <p>Attempts to decode a one-dimensional barcode format given a single row of
* an image.</p>
*
* @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<DecodeHintType, ?>) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> ;
}