mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 20:32:34 +00:00
318 lines
14 KiB
Rust
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>> ;
|
|
}
|
|
|