diff --git a/src/barcode_format.rs b/src/barcode_format.rs index 5ca636c..b1211e2 100644 --- a/src/barcode_format.rs +++ b/src/barcode_format.rs @@ -119,6 +119,7 @@ impl Display for BarcodeFormat { BarcodeFormat::UPC_A => "upc a", BarcodeFormat::UPC_E => "upc e", BarcodeFormat::UPC_EAN_EXTENSION => "upc/ean extension", + BarcodeFormat::DXFilmEdge => "DXFilmEdge", _ => "unsuported", } ) @@ -166,6 +167,7 @@ impl From<&str> for BarcodeFormat { "upc e" | "upc_e" | "upce" => BarcodeFormat::UPC_E, "upc ean extension" | "upc extension" | "ean extension" | "upc/ean extension" | "upc_ean_extension" => BarcodeFormat::UPC_EAN_EXTENSION, + "DXFilmEdge" | "dxfilmedge" | "dx film edge" => BarcodeFormat::DXFilmEdge, _ => BarcodeFormat::UNSUPORTED_FORMAT, } } diff --git a/src/binary_bitmap.rs b/src/binary_bitmap.rs index 056c72b..3f4189e 100644 --- a/src/binary_bitmap.rs +++ b/src/binary_bitmap.rs @@ -32,7 +32,7 @@ use crate::{ pub struct BinaryBitmap { binarizer: B, - matrix: Option, + pub(crate) matrix: Option, } impl BinaryBitmap { diff --git a/src/buffered_image_luminance_source.rs b/src/buffered_image_luminance_source.rs index a736ec1..4d6bb8b 100644 --- a/src/buffered_image_luminance_source.rs +++ b/src/buffered_image_luminance_source.rs @@ -16,7 +16,7 @@ use std::rc::Rc; -use image::{DynamicImage, ImageBuffer, Luma}; +use image::{DynamicImage, GenericImageView, ImageBuffer, Luma, Pixel}; use imageproc::geometric_transformations::rotate_about_center; use crate::common::Result; @@ -108,6 +108,33 @@ impl LuminanceSource for BufferedImageLuminanceSource { pixels } + fn get_column(&self, x: usize) -> Vec { + let width = self.get_height(); // - self.left as usize; + + let pixels: Vec = || -> Option> { + Some( + self.image + .as_luma8()? + .rows() + .skip(self.top as usize) + .fold(Vec::default(), |mut acc, e| { + acc.push( + e.into_iter() + .nth(self.left as usize + x) + .unwrap_or(&Luma([0_u8])), + ); + acc + }) + .iter() + .map(|&p| p.0[0]) + .collect(), + ) + }() + .unwrap_or_default(); + + pixels + } + fn get_matrix(&self) -> Vec { if self.height == self.image.height() as usize && self.width == self.image.width() as usize { @@ -201,4 +228,8 @@ impl LuminanceSource for BufferedImageLuminanceSource { top: 0, }) } + + fn get_luma8_point(&self, x: usize, y: usize) -> u8 { + self.image.get_pixel(x as u32, y as u32).to_luma().0[0] + } } diff --git a/src/common/cpp_essentials/pattern.rs b/src/common/cpp_essentials/pattern.rs index 6e6b288..85f7b45 100644 --- a/src/common/cpp_essentials/pattern.rs +++ b/src/common/cpp_essentials/pattern.rs @@ -217,7 +217,7 @@ impl<'a> PatternView<'a> { pub fn isValidWithN(&self, n: usize) -> bool { !self.data.0.is_empty() && self.start <= self.current + self.start - && self.current + n <= (self.data.0.len()) + && self.current + n < (self.data.0.len()) /*return _data && _data >= _base && _data + n <= _end;*/ } pub fn isValid(&self) -> bool { @@ -271,7 +271,10 @@ impl<'a> PatternView<'a> { } pub fn extend(&mut self) { - self.count = std::cmp::max(0, self.data.len() - (self.current + self.start)) + self.count = std::cmp::max( + 0, + self.data.len() as isize - (self.current + self.start) as isize, + ) as usize } fn try_get_index(&self, index: isize) -> Option { diff --git a/src/common/global_histogram_binarizer.rs b/src/common/global_histogram_binarizer.rs index 722f4ee..dfc12da 100644 --- a/src/common/global_histogram_binarizer.rs +++ b/src/common/global_histogram_binarizer.rs @@ -27,7 +27,7 @@ use once_cell::unsync::OnceCell; use crate::common::Result; use crate::{Binarizer, Exceptions, LuminanceSource}; -use super::{BitArray, BitMatrix}; +use super::{BitArray, BitMatrix, LineOrientation}; const LUMINANCE_BITS: usize = 5; const LUMINANCE_SHIFT: usize = 8 - LUMINANCE_BITS; @@ -107,8 +107,55 @@ impl Binarizer for GlobalHistogramBinarizer { Ok(Cow::Borrowed(row)) } - fn get_black_line(&self, l: usize, lt: super::LineOrientation) -> Result> { - unimplemented!() + fn get_black_line(&self, l: usize, lt: LineOrientation) -> Result> { + if lt == LineOrientation::Row { + self.get_black_row(l) + } else { + let col = self.black_column_cache[l].get_or_try_init(|| { + let source = self.get_luminance_source(); + let width = source.get_height(); + let mut col = BitArray::with_size(width); + + // self.initArrays(width); + let localLuminances = source.get_column(l); + let mut localBuckets = [0; LUMINANCE_BUCKETS]; //self.buckets.clone(); + for x in 0..width { + // for (int x = 0; x < width; x++) { + localBuckets[((localLuminances[x]) >> LUMINANCE_SHIFT) as usize] += 1; + } + let blackPoint = Self::estimateBlackPoint(&localBuckets)?; + + if width < 3 { + // Special case for very small images + for (x, lum) in localLuminances.iter().enumerate().take(width) { + // for x in 0..width { + // for (int x = 0; x < width; x++) { + if (*lum as u32) < blackPoint { + col.set(x); + } + } + } else { + let mut left = localLuminances[0]; // & 0xff; + let mut center = localLuminances[1]; // & 0xff; + for x in 1..width - 1 { + // for (int x = 1; x < width - 1; x++) { + let right = localLuminances[x + 1]; + // A simple -1 4 -1 box filter with a weight of 2. + if ((center as i64 * 4) - left as i64 - right as i64) / 2 + < blackPoint as i64 + { + col.set(x); + } + left = center; + center = right; + } + } + + Ok(col) + })?; + + Ok(Cow::Borrowed(col)) + } } // Does not sharpen the data, as this call is intended to only be used by 2D Readers. diff --git a/src/filtered_image_reader.rs b/src/filtered_image_reader.rs new file mode 100644 index 0000000..ff8dce1 --- /dev/null +++ b/src/filtered_image_reader.rs @@ -0,0 +1,216 @@ +use std::collections::HashMap; + +use crate::{Binarizer, BinaryBitmap, Exceptions, Luma8LuminanceSource, LuminanceSource, Reader}; + +use crate::common::{BitMatrix, Result}; + +pub const DEFAULT_DOWNSCALE_THRESHHOLD: usize = 500; +pub const DEFAULT_DOWNSCALE_FACTOR: usize = 3; + +/// Passed image data is ignored, only the image data +pub struct FilteredImageReader> { + reader: R, + source: Luma8LuminanceSource, + binarizer: B, +} + +impl> FilteredImageReader { + pub fn new>( + reader: R, + source: I, + binarizer: B, + ) -> Self { + Self { + reader, + source: source.into(), + binarizer, + } + } +} + +impl> Reader + for FilteredImageReader +{ + fn decode( + &mut self, + _image: &mut crate::BinaryBitmap, + ) -> crate::common::Result { + self.decode_with_hints(_image, &HashMap::default()) + } + + fn decode_with_hints( + &mut self, + _image: &mut crate::BinaryBitmap, + hints: &crate::DecodingHintDictionary, + ) -> crate::common::Result { + let pyramids = LumImagePyramid::new( + self.source.clone(), + DEFAULT_DOWNSCALE_THRESHHOLD, + DEFAULT_DOWNSCALE_FACTOR, + ) + .ok_or(Exceptions::ILLEGAL_ARGUMENT)?; + for layer in pyramids.layers { + let mut b = BinaryBitmap::new(self.binarizer.create_binarizer(layer)); + for close in [false, true] { + if close { + let Ok(_) = b.close() else { + continue; + }; + } + if let Ok(res) = self.reader.decode_with_hints(&mut b, hints) { + return Ok(res); + } else { + continue; + } + } + } + Err(Exceptions::NOT_FOUND) + } +} + +#[derive(Debug, Clone)] +struct LumImagePyramid { + buffers: Vec, + pub layers: Vec, +} + +impl Default for LumImagePyramid { + fn default() -> Self { + Self { + buffers: Default::default(), + layers: Default::default(), + } + } +} + +impl LumImagePyramid { + pub fn new(image: Luma8LuminanceSource, threshold: usize, factor: usize) -> Option { + let mut new_self = Self::default(); + + new_self.layers.push(image); + // TODO: if only matrix codes were considered, then using std::min would be sufficient (see #425) + while threshold > 0 + && std::cmp::max( + new_self.layers.last()?.get_width(), + new_self.layers.last()?.get_height(), + ) > threshold + && std::cmp::min( + new_self.layers.last()?.get_width(), + new_self.layers.last()?.get_height(), + ) >= factor + { + new_self.add_layer_with_factor(factor).ok()?; + } + + if false { + // Reversing the layers means we'd start with the smallest. that can make sense if we are only looking for a + // single symbol. If we start with the higher resolution, we get better (high res) position information. + // TODO: see if masking out higher res layers based on found symbols in lower res helps overall performance. + new_self.layers.reverse(); + } + + Some(new_self) + } + + fn add_layer(&mut self) -> Result<()> { + let siv = self.layers.last().ok_or(Exceptions::ILLEGAL_ARGUMENT)?; + + self.buffers.push(Luma8LuminanceSource::with_empty_image( + siv.get_width() / N, + siv.get_height() / N, + )); + + let div = self + .buffers + .last_mut() + .ok_or(Exceptions::ILLEGAL_ARGUMENT)?; + + let div_height = div.get_height(); + let div_width = div.get_width(); + + let d_vec = div.get_matrix_mut(); + + for d in d_vec.iter_mut() { + for dy in 0..div_height { + // for (int dy = 0; dy < div.height(); ++dy){ + for dx in 0..div_width { + // for (int dx = 0; dx < div.width(); ++dx) { + let mut sum = (N * N) as u8 / 2; + for ty in 0..N { + // for (int ty = 0; ty < N; ++ty){ + for tx in 0..N { + // for (int tx = 0; tx < N; ++tx) { + sum += siv.get_luma8_point(dx * N + tx, dy * N + ty); + } + } + *d = sum / (N * N) as u8; + } + } + } + + self.layers.push( + self.buffers + .last() + .ok_or(Exceptions::ILLEGAL_ARGUMENT)? + .clone(), + ); + + Ok(()) + } + + fn add_layer_with_factor(&mut self, factor: usize) -> Result<()> { + // help the compiler's auto-vectorizer by hard-coding the scale factor + match factor { + 2 => self.add_layer::<2>(), + 3 => self.add_layer::<3>(), + 4 => self.add_layer::<4>(), + _ => Err(Exceptions::illegal_argument_with( + "Invalid ReaderOptions::downscaleFactor", + )), + } + } +} + +const SET_V: u32 = 0xff; // allows playing with SIMD binarization + +impl BinaryBitmap { + pub fn close(&mut self) -> Result<()> { + if let Some(mut matrix) = self.matrix.as_mut() { + // if (_cache->matrix) { + // auto& matrix = *const_cast(_cache->matrix.get()); + let mut tmp = BitMatrix::new(matrix.width(), matrix.height())?; + + // dilate + SumFilter(matrix, &mut tmp, |sum| { + return u32::from(sum > 0 * SET_V) * SET_V; + }); + // erode + SumFilter(&tmp, &mut matrix, |sum| { + return u32::from(sum == 9 * SET_V) * SET_V; + }); + } + Ok(()) + // _closed = true; + } +} + +fn SumFilter(input: &BitMatrix, output: &mut BitMatrix, func: F) +where + F: Fn(u32) -> u32, +{ + for row in 1..output.height() { + for col in 0..output.width() - 1 { + let in0 = input.getRow(row); //.row(0).begin(); + let in1 = input.getRow(row + 1); //.row(1).begin(); + let in2 = input.getRow(row + 2); //.row(2).begin(); + + let mut sum = 0; + for j in 0..3 { + // for (int j = 0; j < 3; ++j){ + sum += in0.get(j) as u32 + in1.get(j) as u32 + in2.get(j) as u32; + } + + output.set_bool(row, col, func(sum) != 0); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index d6675e2..b312d08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,6 +104,9 @@ pub mod helpers; mod luma_luma_source; pub use luma_luma_source::*; +mod filtered_image_reader; +pub use filtered_image_reader::*; + #[cfg(feature = "svg_read")] mod svg_luminance_source; #[cfg(feature = "svg_read")] diff --git a/src/luma_luma_source.rs b/src/luma_luma_source.rs index 9b525ee..f81a59b 100644 --- a/src/luma_luma_source.rs +++ b/src/luma_luma_source.rs @@ -2,6 +2,7 @@ use crate::common::Result; use crate::LuminanceSource; /// A simple luma8 source for bytes, supports cropping but not rotation +#[derive(Debug, Clone)] pub struct Luma8LuminanceSource { /// image dimension in form (x,y) dimensions: (u32, u32), @@ -27,6 +28,19 @@ impl LuminanceSource for Luma8LuminanceSource { .collect() } + fn get_column(&self, x: usize) -> Vec { + self.data + .chunks_exact(self.original_dimension.0 as usize) + .skip(self.origin.1 as usize) + .fold(Vec::default(), |mut acc, e| { + acc.push(e[self.origin.0 as usize + x as usize]); + acc + }) + .iter() + .map(|byte| Self::invert_if_should(*byte, self.inverted)) + .collect() + } + fn get_matrix(&self) -> Vec { self.data .iter() @@ -93,6 +107,10 @@ impl LuminanceSource for Luma8LuminanceSource { "This luminance source does not support rotation by 45 degrees.", )) } + + fn get_luma8_point(&self, x: usize, y: usize) -> u8 { + todo!() + } } impl Luma8LuminanceSource { @@ -159,6 +177,20 @@ impl Luma8LuminanceSource { } } + pub fn with_empty_image(width: usize, height: usize) -> Self { + Self { + dimensions: (width as u32, height as u32), + origin: (0, 0), + data: vec![0u8; width * height], + inverted: false, + original_dimension: (width as u32, height as u32), + } + } + + pub fn get_matrix_mut(&mut self) -> &mut Vec { + &mut self.data + } + #[inline(always)] fn invert_if_should(byte: u8, invert: bool) -> u8 { if invert { diff --git a/src/luminance_source.rs b/src/luminance_source.rs index 9c62d2e..54ee32d 100644 --- a/src/luminance_source.rs +++ b/src/luminance_source.rs @@ -48,6 +48,9 @@ pub trait LuminanceSource { */ fn get_row(&self, y: usize) -> Vec; + /// Get a column of of the image + fn get_column(&self, x: usize) -> Vec; + /** * Fetches luminance data for the underlying bitmap. Values should be fetched using: * {@code int luminance = array[y * width + x] & 0xff} @@ -149,6 +152,8 @@ pub trait LuminanceSource { iv } + fn get_luma8_point(&self, x: usize, y: usize) -> u8; + /* @Override public final String toString() { diff --git a/src/multi_format_reader.rs b/src/multi_format_reader.rs index 9583d6c..1c017aa 100644 --- a/src/multi_format_reader.rs +++ b/src/multi_format_reader.rs @@ -17,6 +17,7 @@ use std::collections::{HashMap, HashSet}; use crate::common::Result; +use crate::oned::cpp::ODReader; use crate::qrcode::cpp_port::QrReader; use crate::{ aztec::AztecReader, datamatrix::DataMatrixReader, maxicode::MaxiCodeReader, @@ -194,6 +195,9 @@ impl MultiFormatReader { BarcodeFormat::MAXICODE => { MaxiCodeReader::default().decode_with_hints(image, &self.hints) } + BarcodeFormat::DXFilmEdge => { + ODReader::new(&self.hints).decode_with_hints(image, &self.hints) + } _ => Err(Exceptions::UNSUPPORTED_OPERATION), }; if res.is_ok() { @@ -230,6 +234,9 @@ impl MultiFormatReader { if let Ok(res) = MaxiCodeReader::default().decode_with_hints(image, &self.hints) { return Ok(res); } + if let Ok(res) = ODReader::new(&self.hints).decode_with_hints(image, &self.hints) { + return Ok(res); + } if self.try_harder { if let Ok(res) = self.one_d_reader.decode_with_hints(image, &self.hints) { diff --git a/src/oned/cpp/dxfilm_edge_reader.rs b/src/oned/cpp/dxfilm_edge_reader.rs index 5734149..02294e7 100644 --- a/src/oned/cpp/dxfilm_edge_reader.rs +++ b/src/oned/cpp/dxfilm_edge_reader.rs @@ -71,7 +71,7 @@ fn DistIsBelowThreshold(a: PointI, b: PointI, threshold: PointI) -> bool { } // DX Film Edge clock track found on 35mm films. -#[derive(Debug)] +#[derive(Debug, Clone)] pub(super) struct Clock { hasFrameNr: bool, // = false; // Clock track (thus data track) with frame number (longer version) rowNumber: u32, // = 0, diff --git a/src/oned/cpp/one_d_reader.rs b/src/oned/cpp/one_d_reader.rs index 5ffc20f..8f3a690 100644 --- a/src/oned/cpp/one_d_reader.rs +++ b/src/oned/cpp/one_d_reader.rs @@ -17,7 +17,7 @@ use crate::{Binarizer, DecodeHintType, DecodeHintValue}; use crate::common::{LineOrientation, Quadrilateral, Result}; use super::dxfilm_edge_reader::DXFilmEdgeReader; -use super::row_reader::RowReader; +use super::row_reader::{DecodingState, RowReader}; pub struct ODReader<'a> { reader: DXFilmEdgeReader<'a>, // THIS IS WRONG, SEE BELOW ONLY DOES ONE @@ -47,11 +47,11 @@ impl<'a> ODReader<'_> { isPure: bool, maxSymbols: u32, minLineCount: u32, - returnErrors: bool, + _returnErrors: bool, ) -> Vec { let mut res: Vec> = Vec::new(); - let mut decodingState: Vec<&mut Option> = Vec::new(); + let mut decodingState: Vec> = vec![Some(DecodingState::default()); 1]; // std::vector> decodingState(readers.size()); let mut minLineCount = minLineCount; @@ -120,6 +120,7 @@ impl<'a> ODReader<'_> { checkRows.pop(); isCheckRow = true; if (rowNumber < 0 || rowNumber >= height) { + i += 1; continue; } } @@ -134,6 +135,7 @@ impl<'a> ODReader<'_> { ) { r.as_ref().into() } else { + i += 1; continue; }; // let img = if rotate {let a = image.rotate_counter_clockwise(); &a} else {image}; @@ -182,7 +184,7 @@ impl<'a> ODReader<'_> { let mut next = PatternView::new(&bars); loop { let mut result_hld = readers[r] - .decodePattern(rowNumber as u32, &mut next, decodingState[r]) + .decodePattern(rowNumber as u32, &mut next, &mut decodingState[r]) .ok(); if result_hld.is_some() /*|| (returnErrors && result.is_none())*/ @@ -277,6 +279,7 @@ impl<'a> ODReader<'_> { // make sure we make progress and we start the next try on a bar next.shift(2 - (next.index() % 2)); next.extend(); + if !(tryHarder && next.size() > 0) { break; } @@ -371,29 +374,7 @@ impl<'a> ODReader<'_> { _hints: &DecodingHintDictionary, image: &BinaryBitmap, ) -> Result { - let mut result = Self::DoDecode( - &self.reader, - image, - self.try_harder, - false, - self.is_pure, - 1, - self.min_line_count, - self.return_errors, - ); - - if (result.is_empty() && self.try_rotate) { - result = Self::DoDecode( - &self.reader, - image, - self.try_harder, - true, - self.is_pure, - 1, - self.min_line_count, - self.return_errors, - ); - } + let result = self.decode_with_max_symbols(_hints, image, u32::MAX)?; result.first().cloned().ok_or(Exceptions::NOT_FOUND) // return FirstOrDefault(std::move(result)); diff --git a/src/oned/cpp/row_reader.rs b/src/oned/cpp/row_reader.rs index 69260c3..2dc95ec 100644 --- a/src/oned/cpp/row_reader.rs +++ b/src/oned/cpp/row_reader.rs @@ -41,7 +41,7 @@ type Alphabet = Vec; // // virtual ~DecodingState() = default; // } -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] pub struct DecodingState { // DXO pub centerRow: u32, diff --git a/src/planar_yuv_luminance_source.rs b/src/planar_yuv_luminance_source.rs index aff992b..7bdf857 100644 --- a/src/planar_yuv_luminance_source.rs +++ b/src/planar_yuv_luminance_source.rs @@ -256,6 +256,10 @@ impl LuminanceSource for PlanarYUVLuminanceSource { row } + fn get_column(&self, x: usize) -> Vec { + unimplemented!() + } + fn get_matrix(&self) -> Vec { let width = self.get_width(); let height = self.get_height(); @@ -332,4 +336,8 @@ impl LuminanceSource for PlanarYUVLuminanceSource { fn invert(&mut self) { self.invert = !self.invert; } + + fn get_luma8_point(&self, x: usize, y: usize) -> u8 { + unimplemented!() + } } diff --git a/src/rgb_luminance_source.rs b/src/rgb_luminance_source.rs index 144350a..aa95341 100644 --- a/src/rgb_luminance_source.rs +++ b/src/rgb_luminance_source.rs @@ -58,6 +58,10 @@ impl LuminanceSource for RGBLuminanceSource { row } + fn get_column(&self, x: usize) -> Vec { + unimplemented!() + } + fn get_matrix(&self) -> Vec { let width = self.get_width(); let height = self.get_height(); @@ -127,6 +131,14 @@ impl LuminanceSource for RGBLuminanceSource { fn invert(&mut self) { self.invert = !self.invert; } + + fn get_luma8_point(&self, x: usize, y: usize) -> u8 { + let width = self.get_width(); + let row_offset = (y + self.top) * self.dataWidth + self.left; + let col_offset = (x + self.left); + + self.luminances[row_offset + col_offset] + } } impl RGBLuminanceSource { diff --git a/src/svg_luminance_source.rs b/src/svg_luminance_source.rs index dcd7ab8..02dd03f 100644 --- a/src/svg_luminance_source.rs +++ b/src/svg_luminance_source.rs @@ -11,6 +11,10 @@ impl LuminanceSource for SVGLuminanceSource { self.0.get_row(y) } + fn get_column(&self, x: usize) -> Vec { + self.0.get_column(x) + } + fn get_matrix(&self) -> Vec { self.0.get_matrix() } @@ -46,6 +50,10 @@ impl LuminanceSource for SVGLuminanceSource { fn rotate_counter_clockwise_45(&self) -> Result { self.0.rotate_counter_clockwise_45().map(Self) } + + fn get_luma8_point(&self, x: usize, y: usize) -> u8 { + self.0.get_luma8_point(x, y) + } } impl SVGLuminanceSource { diff --git a/test_resources/blackbox/dxfilmedge-1/1.jpg b/test_resources/blackbox/dxfilmedge-1/1.jpg new file mode 100644 index 0000000..9f34211 Binary files /dev/null and b/test_resources/blackbox/dxfilmedge-1/1.jpg differ diff --git a/test_resources/blackbox/dxfilmedge-1/1.txt b/test_resources/blackbox/dxfilmedge-1/1.txt new file mode 100644 index 0000000..49f6980 --- /dev/null +++ b/test_resources/blackbox/dxfilmedge-1/1.txt @@ -0,0 +1 @@ +36-2/21 \ No newline at end of file diff --git a/test_resources/blackbox/dxfilmedge-1/2.png b/test_resources/blackbox/dxfilmedge-1/2.png new file mode 100644 index 0000000..665f080 Binary files /dev/null and b/test_resources/blackbox/dxfilmedge-1/2.png differ diff --git a/test_resources/blackbox/dxfilmedge-1/2.txt b/test_resources/blackbox/dxfilmedge-1/2.txt new file mode 100644 index 0000000..a2b1434 --- /dev/null +++ b/test_resources/blackbox/dxfilmedge-1/2.txt @@ -0,0 +1 @@ +80-11/23 \ No newline at end of file diff --git a/test_resources/blackbox/dxfilmedge-1/3.jpg b/test_resources/blackbox/dxfilmedge-1/3.jpg new file mode 100644 index 0000000..ce106b7 Binary files /dev/null and b/test_resources/blackbox/dxfilmedge-1/3.jpg differ diff --git a/test_resources/blackbox/dxfilmedge-1/3.txt b/test_resources/blackbox/dxfilmedge-1/3.txt new file mode 100644 index 0000000..2c0e0b1 --- /dev/null +++ b/test_resources/blackbox/dxfilmedge-1/3.txt @@ -0,0 +1 @@ +10-3 \ No newline at end of file diff --git a/tests/dx_film_edge.rs b/tests/dx_film_edge.rs new file mode 100644 index 0000000..e0d6037 --- /dev/null +++ b/tests/dx_film_edge.rs @@ -0,0 +1,27 @@ +// dxfilmedge-1 + +#![cfg(feature = "image")] + +use rxing::{BarcodeFormat, MultiFormatReader}; + +mod common; + +/** + * @author Sean Owen + */ +#[cfg(feature = "image-formats")] +#[test] +fn dx_film_edge() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/dxfilmedge-1", + MultiFormatReader::default(), + BarcodeFormat::DXFilmEdge, + ); + + tester.add_test(1, 2, 0.0); + tester.add_test(1, 2, 90.0); + tester.add_test(1, 2, 180.0); + tester.add_test(1, 2, 320.0); + + tester.test_black_box() +}