diff --git a/Cargo.toml b/Cargo.toml index ba44b95..17aa603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ exclude = [ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -regex = "1.7.1" +regex = "1.8.0" fancy-regex = "0.11" once_cell = "1.17.1" encoding = "0.2" @@ -31,6 +31,7 @@ svg = {version = "0.13", optional = true} resvg = {version = "0.28.0", optional = true, default-features=false} serde = { version = "1.0", features = ["derive", "rc"], optional = true } thiserror = "1.0.38" +multimap = "0.8.3" [dev-dependencies] java-properties = "1.4.1" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 45e3d1f..07670df 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -10,5 +10,5 @@ keywords = ["barcode", "2d_barcode", "1d_barcode", "barcode_reader", "barcode_wr # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.1.1", features = ["derive"] } +clap = { version = "4.2.0", features = ["derive"] } rxing = {path = "../../", version = "~0.4.0", features = ["image", "svg_read", "svg_write"] } diff --git a/src/aztec/DetectorTest.rs b/src/aztec/DetectorTest.rs index cd93c31..5a21d0d 100644 --- a/src/aztec/DetectorTest.rs +++ b/src/aztec/DetectorTest.rs @@ -115,12 +115,18 @@ fn test_error_in_parameter_locator(data: &str) { // dbg!(copy.to_string()); // dbg!(make_larger(©, 3).to_string()); // The detector doesn't seem to work when matrix bits are only 1x1. So magnify. + // dbg!(isMirror, error1, error2); let r = Detector::new(&make_larger(©, 3)).detect(isMirror); - assert!(r.is_ok()); + assert!( + r.is_ok(), + "decode should be ok: {isMirror:?}, {error1}, {error2}" + ); let r = r.expect("result already tested as ok"); assert_eq!(r.getNbLayers(), layers); assert_eq!(r.isCompact(), compact); - let res = decoder::decode(&r).expect("decode should be ok"); + let res = decoder::decode(&r).unwrap_or_else(|_| { + panic!("decode should be ok: {isMirror}, {error1}, {error2}") + }); assert_eq!(data, res.getText()); } } diff --git a/src/aztec/detector.rs b/src/aztec/detector.rs index b0bc4a2..3bb8766 100644 --- a/src/aztec/detector.rs +++ b/src/aztec/detector.rs @@ -469,7 +469,9 @@ impl<'a> Detector<'_> { point(low, high), ); - sampler.sample_grid_detailed(image, dimension, dimension, dst, quad) + let (res, _) = sampler.sample_grid_detailed(image, dimension, dimension, dst, quad)?; + + Ok(res) } /** diff --git a/src/barcode_format.rs b/src/barcode_format.rs index 0f1f45f..43b0464 100644 --- a/src/barcode_format.rs +++ b/src/barcode_format.rs @@ -65,6 +65,8 @@ pub enum BarcodeFormat { /** QR Code 2D barcode format. */ QR_CODE, + MICRO_QR_CODE, + /** RSS 14 */ RSS_14, @@ -102,6 +104,7 @@ impl Display for BarcodeFormat { BarcodeFormat::MAXICODE => "maxicode", BarcodeFormat::PDF_417 => "pdf 417", BarcodeFormat::QR_CODE => "qrcode", + BarcodeFormat::MICRO_QR_CODE => "mqr", BarcodeFormat::RSS_14 => "rss 14", BarcodeFormat::RSS_EXPANDED => "rss expanded", BarcodeFormat::UPC_A => "upc a", @@ -140,6 +143,9 @@ impl From<&str> for BarcodeFormat { "maxicode" | "maxi_code" => BarcodeFormat::MAXICODE, "pdf 417" | "pdf_417" | "pdf417" | "iso 15438" | "iso_15438" => BarcodeFormat::PDF_417, "qrcode" | "qr_code" | "qr code" => BarcodeFormat::QR_CODE, + "mqr" | "microqr" | "micro_qr" | "micro_qrcode" | "micro_qr_code" | "mqr_code" => { + BarcodeFormat::MICRO_QR_CODE + } "rss 14" | "rss_14" | "rss14" | "gs1 databar" | "gs1 databar coupon" | "gs1_databar_coupon" => BarcodeFormat::RSS_14, "rss expanded" | "expanded rss" | "rss_expanded" => BarcodeFormat::RSS_EXPANDED, diff --git a/src/common/bit_array.rs b/src/common/bit_array.rs index 225c74c..3f0bc4e 100644 --- a/src/common/bit_array.rs +++ b/src/common/bit_array.rs @@ -18,6 +18,7 @@ // import java.util.Arrays; +use std::ops::Index; use std::{cmp, fmt}; use crate::common::Result; @@ -82,6 +83,14 @@ impl BitArray { (self.bits[i / 32] & (1 << (i & 0x1F))) != 0 } + pub fn try_get(&self, i: usize) -> Option { + if (i / 32) >= self.bits.len() { + None + } else { + Some(self.get(i)) + } + } + /** * Sets bit i. * @@ -401,3 +410,30 @@ impl Default for BitArray { Self::new() } } + +impl Into> for BitArray { + fn into(self) -> Vec { + let mut array = vec![0; self.getSizeInBytes()]; + self.toBytes(0, &mut array, 0, self.getSizeInBytes()); + array + // let mut arr = vec![0; self.get_size()]; + // for x in 0..self.get_size() { + // if self.get(x) { + // arr[x] = 1; + // } + // } + // arr + } +} + +impl Into> for BitArray { + fn into(self) -> Vec { + let mut array = vec![false; self.size]; + + for pixel in 0..self.size { + array[pixel] = bool::from(self.get(pixel)); + } + + array + } +} diff --git a/src/common/bit_matrix.rs b/src/common/bit_matrix.rs index 2131927..00259db 100644 --- a/src/common/bit_matrix.rs +++ b/src/common/bit_matrix.rs @@ -21,7 +21,7 @@ use std::fmt; use crate::common::Result; -use crate::{point, Exceptions, Point}; +use crate::{point, point_i, Exceptions, Point}; use super::BitArray; @@ -222,6 +222,17 @@ impl BitMatrix { // ((self.bits[offset] >> (x & 0x1f)) & 1) != 0 } + pub fn get_index>(&self, index: T) -> bool { + self.get_point(self.calculate_point_from_index(index.into())) + } + + #[inline(always)] + fn calculate_point_from_index(&self, index: usize) -> Point { + let row = index / (self.getWidth() as usize); + let column = index % (self.getWidth() as usize); + point_i(column as u32, row as u32) + } + #[inline(always)] fn get_offset(&self, y: u32, x: u32) -> usize { y as usize * self.row_size + (x as usize / 32) @@ -420,6 +431,21 @@ impl BitMatrix { rw } + /// This method returns a column of the bitmatrix. + /// + /// The current implementation may be very slow. + pub fn getCol(&self, x: u32) -> BitArray { + let mut cw = BitArray::with_size(self.height as usize); + + for y in 0..self.height { + if self.get(x, y) { + cw.set(y as usize) + } + } + + cw + } + /** * @param y row to set * @param row {@link BitArray} to copy from @@ -607,6 +633,10 @@ impl BitMatrix { * @return The width of the matrix */ pub fn getWidth(&self) -> u32 { + self.width() + } + + pub fn width(&self) -> u32 { self.width } @@ -614,6 +644,10 @@ impl BitMatrix { * @return The height of the matrix */ pub fn getHeight(&self) -> u32 { + self.height() + } + + pub fn height(&self) -> u32 { self.height } @@ -707,6 +741,10 @@ impl BitMatrix { new_bm } + pub fn is_in(&self, p: Point) -> bool { + self.isIn(p, 0) + } + pub fn isIn(&self, p: Point, b: i32) -> bool { b as f32 <= p.x && p.x < self.getWidth() as f32 - b as f32 @@ -721,6 +759,19 @@ impl fmt::Display for BitMatrix { } } +impl From<&BitMatrix> for Vec { + fn from(value: &BitMatrix) -> Self { + let mut arr = vec![false; (value.width * value.height) as usize]; + for x in 0..value.width { + for y in 0..value.height { + let insert_pos = ((y * value.width) + x) as usize; + arr[insert_pos] = value.get(x, y); + } + } + arr + } +} + #[cfg(feature = "image")] /// This should only be used if you *know* that the `DynamicImage` is binary. impl TryFrom for BitMatrix { diff --git a/src/common/bit_source.rs b/src/common/bit_source.rs index 7e10f4e..cfb4174 100644 --- a/src/common/bit_source.rs +++ b/src/common/bit_source.rs @@ -116,6 +116,56 @@ impl BitSource { Ok(result) } + pub fn peak_bits(&self, numBits: usize) -> Result { + if !(1..=32).contains(&numBits) || numBits > self.available() { + return Err(Exceptions::illegal_argument_with(numBits.to_string())); + } + + let mut bit_offset = self.bit_offset; + let mut byte_offset = self.byte_offset; + + let mut result: u32 = 0; + + let mut num_bits = numBits; + + // First, read remainder from current byte + if self.bit_offset > 0 { + let bitsLeft = 8 - self.bit_offset; + let toRead = cmp::min(num_bits, bitsLeft); + let bitsToNotRead = bitsLeft - toRead; + let mask = (0xFF >> (8 - toRead)) << bitsToNotRead; + + result = (self.bytes[self.byte_offset] & mask) as u32 >> bitsToNotRead; + num_bits -= toRead; + bit_offset += toRead; + if bit_offset == 8 { + bit_offset = 0; + byte_offset += 1; + } + } + + // Next read whole bytes + if num_bits > 0 { + while num_bits >= 8 { + result = (result << 8) | self.bytes[byte_offset] as u32; + // result = ((result as u16) << 8) as u8 | (self.bytes[self.byte_offset]); + byte_offset += 1; + num_bits -= 8; + } + + // Finally read a partial byte + if num_bits > 0 { + let bits_to_not_read = 8 - num_bits; + let mask = (0xFF >> bits_to_not_read) << bits_to_not_read; + result = (result << num_bits) + | ((self.bytes[byte_offset] & mask) as u32 >> bits_to_not_read); + bit_offset += num_bits; + } + } + + Ok(result) + } + /** * @return number of bits that can be read successfully */ diff --git a/src/common/cpp_essentials/base_extentions/bitmatrix.rs b/src/common/cpp_essentials/base_extentions/bitmatrix.rs new file mode 100644 index 0000000..df9f399 --- /dev/null +++ b/src/common/cpp_essentials/base_extentions/bitmatrix.rs @@ -0,0 +1,102 @@ +use crate::common::BitMatrix; +use crate::common::Result; +use crate::point; +use crate::Point; + +impl BitMatrix { + pub fn Deflate( + &self, + width: u32, + height: u32, + top: f32, + left: f32, + subSampling: f32, + ) -> Result { + let mut result = BitMatrix::new(width, height)?; + + for y in 0..result.height() { + // for (int y = 0; y < result.height(); y++) { + let yOffset = top + y as f32 * subSampling; + for x in 0..result.width() { + // for (int x = 0; x < result.width(); x++) { + if self.get_point(point(left + x as f32 * subSampling, yOffset)) { + result.set(x, y); + } + } + } + + Ok(result) + } + + pub fn getTopLeftOnBitWithPosition(&self, left: &mut u32, top: &mut u32) -> bool { + let Some(Point{x,y}) = self.getTopLeftOnBit() else { + return false; + }; + *left = x as u32; + *top = y as u32; + + true + } + + pub fn getBottomRightOnBitWithPosition(&self, right: &mut u32, bottom: &mut u32) -> bool { + let Some(Point{x,y}) = self.getBottomRightOnBit() else { + return false; + }; + *right = x as u32; + *bottom = y as u32; + + true + } + + pub fn findBoundingBox( + &self, + left: u32, + top: u32, + width: u32, + height: u32, + minSize: u32, + ) -> (bool, u32, u32, u32, u32) { + let mut left = left; + let mut top = top; + let mut width = width; + let mut height = height; + + let mut right = 0; + let mut bottom = 0; + if (!self.getTopLeftOnBitWithPosition(&mut left, &mut top) + || !self.getBottomRightOnBitWithPosition(&mut right, &mut bottom) + || bottom - top + 1 < minSize) + { + return (false, left, top, width, height); + } + + for y in top..=bottom { + // for (int y = top; y <= bottom; y++ ) { + for x in 0..left { + // for (int x = 0; x < left; ++x){ + if (self.get(x, y)) { + left = x; + break; + } + } + for x in (right..(self.width() - 1)).rev() { + // for (int x = _width-1; x > right; x--){ + if (self.get(x, y)) { + right = x; + break; + } + } + } + + width = right - left + 1; + height = bottom - top + 1; + + ( + width >= minSize && height >= minSize, + left, + top, + width, + height, + ) + } +} diff --git a/src/common/cpp_essentials/base_extentions/mod.rs b/src/common/cpp_essentials/base_extentions/mod.rs new file mode 100644 index 0000000..5c34e18 --- /dev/null +++ b/src/common/cpp_essentials/base_extentions/mod.rs @@ -0,0 +1,4 @@ +mod bitmatrix; +mod qr_ec_level; +mod qr_formatinformation; +mod qrcode_version; diff --git a/src/common/cpp_essentials/base_extentions/qr_ec_level.rs b/src/common/cpp_essentials/base_extentions/qr_ec_level.rs new file mode 100644 index 0000000..ee582da --- /dev/null +++ b/src/common/cpp_essentials/base_extentions/qr_ec_level.rs @@ -0,0 +1,31 @@ +use crate::common::Result; +use crate::qrcode::decoder::ErrorCorrectionLevel; + +impl ErrorCorrectionLevel { + pub fn ECLevelFromBitsSigned(bits: i8, isMicro: bool) -> Self { + if (isMicro) { + let LEVEL_FOR_BITS: [ErrorCorrectionLevel; 8] = [ + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::M, + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::M, + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::M, + ErrorCorrectionLevel::Q, + ]; + return LEVEL_FOR_BITS[bits as usize & 0x07]; + } + let LEVEL_FOR_BITS: [ErrorCorrectionLevel; 4] = [ + ErrorCorrectionLevel::M, + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::H, + ErrorCorrectionLevel::Q, + ]; + return LEVEL_FOR_BITS[bits as usize & 0x3]; + } + + pub fn ECLevelFromBits(bits: u8, isMicro: bool) -> Self { + Self::ECLevelFromBitsSigned(bits as i8, isMicro) + } +} diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs new file mode 100644 index 0000000..b021306 --- /dev/null +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -0,0 +1,133 @@ +use crate::common::Result; + +use crate::qrcode::decoder::{ + ErrorCorrectionLevel, FormatInformation, FORMAT_INFO_DECODE_LOOKUP, FORMAT_INFO_MASK_QR, +}; + +pub const FORMAT_INFO_DECODE_LOOKUP_MICRO: [[u32; 2]; 32] = [ + [0x4445, 0x00], + [0x4172, 0x01], + [0x4E2B, 0x02], + [0x4B1C, 0x03], + [0x55AE, 0x04], + [0x5099, 0x05], + [0x5FC0, 0x06], + [0x5AF7, 0x07], + [0x6793, 0x08], + [0x62A4, 0x09], + [0x6DFD, 0x0A], + [0x68CA, 0x0B], + [0x7678, 0x0C], + [0x734F, 0x0D], + [0x7C16, 0x0E], + [0x7921, 0x0F], + [0x06DE, 0x10], + [0x03E9, 0x11], + [0x0CB0, 0x12], + [0x0987, 0x13], + [0x1735, 0x14], + [0x1202, 0x15], + [0x1D5B, 0x16], + [0x186C, 0x17], + [0x2508, 0x18], + [0x203F, 0x19], + [0x2F66, 0x1A], + [0x2A51, 0x1B], + [0x34E3, 0x1C], + [0x31D4, 0x1D], + [0x3E8D, 0x1E], + [0x3BBA, 0x1F], +]; + +impl FormatInformation { + /** + * @param formatInfoBits1 format info indicator, with mask still applied + * @param formatInfoBits2 second copy of same info; both are checked at the same time to establish best match + */ + pub fn DecodeQR(formatInfoBits1: u32, formatInfoBits2: u32) -> Self { + // maks out the 'Dark Module' for mirrored and non-mirrored case (see Figure 25 in ISO/IEC 18004:2015) + let mirroredFormatInfoBits2 = Self::MirrorBits( + ((formatInfoBits2 >> 1) & 0b111111110000000) | (formatInfoBits2 & 0b1111111), + ); + let formatInfoBits2 = + ((formatInfoBits2 >> 1) & 0b111111100000000) | (formatInfoBits2 & 0b11111111); + let mut fi = Self::FindBestFormatInfo( + FORMAT_INFO_MASK_QR, + FORMAT_INFO_DECODE_LOOKUP, + &[ + formatInfoBits1, + formatInfoBits2, + Self::MirrorBits(formatInfoBits1), + mirroredFormatInfoBits2, + ], + ); + + // Use bits 3/4 for error correction, and 0-2 for mask. + fi.error_correction_level = + ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 3) & 0x03, false); + fi.data_mask = fi.index & 0x07; + fi.isMirrored = fi.bitsIndex > 1; + + fi + } + + pub fn DecodeMQR(formatInfoBits: u32) -> Self { + // We don't use the additional masking (with 0x4445) to work around potentially non complying MicroQRCode encoders + let mut fi = Self::FindBestFormatInfo( + 0, + FORMAT_INFO_DECODE_LOOKUP_MICRO, + &[formatInfoBits, Self::MirrorBits(formatInfoBits)], + ); + + const BITS_TO_VERSION: [u8; 8] = [1, 2, 2, 3, 3, 4, 4, 4]; + + // Bits 2/3/4 contain both error correction level and version, 0/1 contain mask. + fi.error_correction_level = + ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 2) & 0x07, true); + fi.data_mask = (fi.index & 0x03); + fi.microVersion = BITS_TO_VERSION[((fi.index >> 2) & 0x07) as usize] as u32; + fi.isMirrored = fi.bitsIndex == 1; + + fi + } + + #[inline(always)] + pub fn MirrorBits(bits: u32) -> u32 { + (bits.reverse_bits()) >> 17 + } + + pub fn FindBestFormatInfo(mask: u32, lookup: [[u32; 2]; 32], bits: &[u32]) -> Self { + let mut fi = FormatInformation::default(); + + // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. + for mask in [0, mask] { + // for (auto mask : {0, mask}) + for bitsIndex in 0..bits.len() { + // for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex) + for [pattern, index] in lookup { + // for (const auto& [pattern, index] : lookup) { + // Find the int in lookup with fewest bits differing + let hammingDist = ((bits[bitsIndex] ^ mask) ^ pattern).count_ones(); + if hammingDist < fi.hammingDistance { + // if (int hammingDist = BitHacks::CountBitsSet((bits[bitsIndex] ^ mask) ^ pattern); hammingDist < fi.hammingDistance) { + fi.index = index as u8; + fi.hammingDistance = hammingDist; + fi.bitsIndex = bitsIndex as u8; + } + } + } + } + + fi + } + + // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match + pub fn isValid(&self) -> bool { + self.hammingDistance <= 3 + } + + pub fn cpp_eq(&self, other: &Self) -> bool { + self.data_mask == other.data_mask + && self.error_correction_level == other.error_correction_level + } +} diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs new file mode 100644 index 0000000..4e1ba46 --- /dev/null +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -0,0 +1,97 @@ +use crate::common::Result; +use crate::qrcode::decoder::{Version, VersionRef, MICRO_VERSIONS, VERSIONS, VERSION_DECODE_INFO}; +use crate::Exceptions; + +// const Version* Version::AllMicroVersions() +// { +// /** +// * See ISO 18004:2006 6.5.1 Table 9 +// */ +// static const Version allVersions[] = { +// {1, {2, 1, 3, 0, 0}}, +// {2, {5, 1, 5, 0, 0, 6, 1, 4, 0, 0}}, +// {3, {6, 1, 11, 0, 0, 8, 1, 9, 0, 0}}, +// {4, {8, 1, 16, 0, 0, 10, 1, 14, 0, 0, 14, 1, 10, 0, 0}}}; +// return allVersions; +// } + +impl Version { + pub fn FromDimension(dimension: u32) -> Result { + let isMicro = dimension < 21; + if (dimension % Self::DimensionStep(isMicro) != 1) { + //throw std::invalid_argument("Unexpected dimension"); + return Err(Exceptions::ILLEGAL_ARGUMENT); + } + return Self::FromNumber( + (dimension - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro), + isMicro, + ); + } + + pub fn FromNumber(versionNumber: u32, is_micro: bool) -> Result { + if (versionNumber < 1 || versionNumber > (if is_micro { 4 } else { 40 })) { + //throw std::invalid_argument("Version should be in range [1-40]."); + return Err(Exceptions::ILLEGAL_ARGUMENT); + } + + Ok(if is_micro { + &MICRO_VERSIONS[versionNumber as usize - 1] + } else { + &VERSIONS[versionNumber as usize - 1] + }) + } + + pub fn DimensionOfVersion(version: u32, is_micro: bool) -> u32 { + Self::DimensionOffset(is_micro) + Self::DimensionStep(is_micro) * version + } + + pub fn DimensionOffset(is_micro: bool) -> u32 { + match is_micro { + true => 9, + false => 17, + } + // return std::array{17, 9}[isMicro]; + } + + pub fn DimensionStep(is_micro: bool) -> u32 { + match is_micro { + true => 2, + false => 4, + } + // return std::array{4, 2}[isMicro]; + } + pub fn DecodeVersionInformation(versionBitsA: i32, versionBitsB: i32) -> Result { + let mut bestDifference = u32::MAX; + let mut bestVersion = 0; + let mut i = 0; + for targetVersion in VERSION_DECODE_INFO { + // for (int targetVersion : VERSION_DECODE_INFO) { + // Do the version info bits match exactly? done. + if targetVersion == versionBitsA as u32 || targetVersion == versionBitsB as u32 { + return Self::getVersionForNumber(i + 7); + } + // Otherwise see if this is the closest to a real version info bit string + // we have seen so far + for bits in [versionBitsA, versionBitsB] { + // for (int bits : {versionBitsA, versionBitsB}) { + let bitsDifference = ((bits as u32) ^ targetVersion).count_ones(); //BitHacks::CountBitsSet(bits ^ targetVersion); + if bitsDifference < bestDifference { + bestVersion = i + 7; + bestDifference = bitsDifference; + } + } + i += 1; + } + // We can tolerate up to 3 bits of error since no two version info codewords will + // differ in less than 8 bits. + if bestDifference <= 3 { + return Self::getVersionForNumber(bestVersion); + } + // If we didn't find a close enough match, fail + return Err(Exceptions::ILLEGAL_STATE); + } + + pub const fn isMicroQRCode(&self) -> bool { + self.is_micro + } +} diff --git a/src/common/cpp_essentials/bitmatrix_cursor.rs b/src/common/cpp_essentials/bitmatrix_cursor.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/common/cpp_essentials/bitmatrix_cursor.rs @@ -0,0 +1 @@ + diff --git a/src/datamatrix/detector/zxing_cpp_detector/bitmatrix_cursor.rs b/src/common/cpp_essentials/bitmatrix_cursor_trait.rs similarity index 77% rename from src/datamatrix/detector/zxing_cpp_detector/bitmatrix_cursor.rs rename to src/common/cpp_essentials/bitmatrix_cursor_trait.rs index 420eb2f..4f4205e 100644 --- a/src/datamatrix/detector/zxing_cpp_detector/bitmatrix_cursor.rs +++ b/src/common/cpp_essentials/bitmatrix_cursor_trait.rs @@ -1,4 +1,4 @@ -use crate::Point; +use crate::{common::BitMatrix, Point}; use super::{util::opposite, Direction, Value}; @@ -8,7 +8,7 @@ use super::{util::opposite, Direction, Value}; * The current position and direction is a PointT. So depending on the type it can be used to traverse the image * in a Bresenham style (PointF) or in a discrete way (step only horizontal/vertical/diagonal (PointI)). */ -pub trait BitMatrixCursor { +pub trait BitMatrixCursorTrait { // const BitMatrix* img; // POINT p; // current position @@ -77,12 +77,13 @@ pub trait BitMatrixCursor { // return isIn(p); // } - fn movedBy(self, d: Point) -> Self; - // { - // auto res = *this; - // res.p += d; - // return res; - // } + fn movedBy(self, d: Point) -> Self; + fn turnedBack(&self) -> Self; // { return {*img, p, back()}; } + // { + // auto res = *this; + // res.p += d; + // return res; + // } /** * @brief stepToEdge advances cursor to one step behind the next (or n-th) edge. @@ -160,20 +161,40 @@ pub trait BitMatrixCursor { res } - // template - // ARRAY readPattern(int range = 0) - // { - // ARRAY res; - // for (auto& i : res) - // i = stepToEdge(1, range); - // return res; - // } + fn p(&self) -> Point; - // template - // ARRAY readPatternFromBlack(int maxWhitePrefix, int range = 0) - // { - // if (maxWhitePrefix && isWhite() && !stepToEdge(1, maxWhitePrefix)) - // return {}; - // return readPattern(range); - // } + fn d(&self) -> Point; + + fn img(&self) -> &BitMatrix; + + fn readPattern + Default + Copy + Clone>( + &mut self, + range: Option, + ) -> Option<[T; LEN]> { + let range = range.unwrap_or(0); + let mut res = [T::default(); LEN]; + for i in res.iter_mut() { + *i = self + .stepToEdge(Some(1), Some(range), None) + .try_into() + .ok()?; + } + Some(res) + } + + fn readPatternFromBlack + Default + Copy + Clone>( + &mut self, + maxWhitePrefix: i32, + range: Option, + ) -> Option<[T; LEN]> { + let range = range.unwrap_or(0); + if (maxWhitePrefix != 0 + && self.isWhite() + && !self.stepToEdge(Some(1), Some(maxWhitePrefix), None) > 0) + { + return None; + } + // return readPattern(range); + self.readPattern::(Some(range)) + } } diff --git a/src/common/cpp_essentials/concentric_finder.rs b/src/common/cpp_essentials/concentric_finder.rs new file mode 100644 index 0000000..29b21fd --- /dev/null +++ b/src/common/cpp_essentials/concentric_finder.rs @@ -0,0 +1,682 @@ +use crate::{ + common::{ + cpp_essentials::{ + Direction, FixedPattern, IsPattern, PatternRow, PatternType, PatternView, + }, + BitMatrix, Quadrilateral, + }, + point, Exceptions, Point, +}; + +use crate::common::Result; + +use super::{ + BitMatrixCursorTrait, EdgeTracer, FastEdgeToEdgeCounter, Pattern, RegressionLine, + RegressionLineTrait, UpdateMinMax, UpdateMinMaxFloat, +}; + +pub fn CenterFromEnd + std::iter::Sum + Copy>( + pattern: &[T; N], + end: f32, +) -> f32 { + if N == 5 { + let a: f32 = pattern[4].into() + pattern[3].into() + pattern[2].into() / 2.0; + let b: f32 = + pattern[4].into() + (pattern[3].into() + pattern[2].into() + pattern[1].into()) / 2.0; + let c: f32 = (pattern[4].into() + + pattern[3].into() + + pattern[2].into() + + pattern[1].into() + + pattern[0].into()) + / 2.0; + end - (2.0 * a + b + c) / 4.0 + } else if N == 3 { + let a: f32 = pattern[2].into() + pattern[1].into() / 2.0; + let b: f32 = (pattern[2].into() + pattern[1].into() + pattern[0].into()) / 2.0; + end - (2.0 * a + b) / 3.0 + } else { + // aztec + let a: f32 = + pattern.iter().skip(N / 2 + 1).copied().sum::().into() + pattern[N / 2].into() / 2.0; + // let a = std::accumulate(pattern.begin() + (N/2 + 1), pattern.end(), pattern[N/2] / 2.0); + end - a + } +} + +pub fn ReadSymmetricPattern( + cur: &mut Cursor, + range: i32, +) -> Option> { + assert!(N % 2 == 1); + + assert!(range > 0); + + let mut range = range; + + let mut res: Pattern = [0; N]; + let s_2 = res.len() as isize / 2; + let mut cuo = cur.turnedBack(); + + let mut next = |cur: &mut Cursor, i: isize| { + let v = cur.stepToEdge(Some(1), Some(range), None); + res[(s_2 + i) as usize] = (res[(s_2 + i) as usize] as i32 + v) as u16; + // res[(s_2 + i) as usize] += v; + if range != 0 { + range -= v; + } + + v + }; + + for i in 0..=s_2 { + // for (int i = 0; i <= s_2; ++i) { + if !next(cur, i) == 0 || !next(&mut cuo, -i) == 0 { + return None; + } + } + res[s_2 as usize] -= 1; // the starting pixel has been counted twice, fix this + + Some(res) +} + +// default for RELAXED_THRESHOLD should be false +pub fn CheckSymmetricPattern< + const E2E: bool, + const LEN: usize, + const SUM: usize, + T: BitMatrixCursorTrait, +>( + cur: &mut T, + pattern: &Pattern, + range: i32, + updatePosition: bool, +) -> i32 { + let mut range = range; + + let mut curFwd: FastEdgeToEdgeCounter = FastEdgeToEdgeCounter::new(cur); + let binding = cur.turnedBack(); + let mut curBwd: FastEdgeToEdgeCounter = FastEdgeToEdgeCounter::new(&binding); + + let centerFwd = curFwd.stepToNextEdge(range as u32) as i32; + if centerFwd == 0 { + return 0; + } + let centerBwd = curBwd.stepToNextEdge(range as u32) as i32; + if centerBwd == 0 { + return 0; + } + + assert!(range > 0); + let mut res: PatternRow = PatternRow::new(vec![0; LEN]); + let s_2 = (res.len()) / 2; + res[s_2] = (centerFwd + centerBwd - 1) as u16; // -1 because the starting pixel is counted twice + range -= res[s_2] as i32; + + let mut next = |cur: &mut FastEdgeToEdgeCounter, i: isize| { + let v = cur.stepToNextEdge(range as u32) as i32; + res[(s_2 as isize + i) as usize] = v as u16; + range -= v; + + v + }; + + for i in 1..=s_2 { + // for (int i = 1; i <= s_2; ++i) { + if next(&mut curFwd, i as isize) == 0 || next(&mut curBwd, -(i as isize)) == 0 { + return 0; + } + } + + if IsPattern::( + &PatternView::new(&res), + &FixedPattern::::with_reference(pattern), + None, + 0.0, + 0.0, + ) == 0.0 + { + return 0; + } + + if updatePosition { + cur.step(Some((res[s_2] as i32 / 2 - (centerBwd - 1)) as f32)); + } + + res.into_iter().sum::() as i32 +} + +pub fn AverageEdgePixels( + cur: &mut T, + range: i32, + numOfEdges: u32, +) -> Option { + let mut sum = Point::default(); + + for _i in 0..numOfEdges { + // for (int i = 0; i < numOfEdges; ++i) { + if !cur.isInSelf() { + return None; + } + cur.stepToEdge(Some(1), Some(range), None); + sum += cur.p().centered() + (cur.p() + cur.back()).centered() + // sum += centered(cur.p) + centered(cur.p + cur.back()); + // log(cur.p + cur.back(), 2); + } + Some(sum / (2 * numOfEdges) as f32) +} + +pub fn CenterOfDoubleCross( + image: &BitMatrix, + center: Point, + range: i32, + numOfEdges: u32, +) -> Option { + let mut sum = Point::default(); + + for d in [ + point(0.0, 1.0), + point(1.0, 0.0), + point(1.0, 1.0), + point(1.0, -1.0), + ] { + // for (auto d : {PointI{0, 1}, {1, 0}, {1, 1}, {1, -1}}) { + let avr1 = AverageEdgePixels(&mut EdgeTracer::new(image, center, d), range, numOfEdges)?; + let avr2 = AverageEdgePixels(&mut EdgeTracer::new(image, center, -d), range, numOfEdges)?; + + sum += avr1 + avr2; + } + Some(sum / 8.0) +} + +pub fn CenterOfRing( + image: &BitMatrix, + center: Point, + range: i32, + nth: i32, + requireCircle: bool, +) -> Option { + // range is the approximate width/height of the nth ring, if nth>1 then it would be plausible to limit the search radius + // to approximately range / 2 * sqrt(2) == range * 0.75 but it turned out to be too limiting with realworld/noisy data. + let radius = range; + let inner = nth < 0; + let nth = nth.abs(); + // log(center, 3); + let mut cur = EdgeTracer::new(image, center, point(0.0, 1.0)); + if cur.stepToEdge(Some(nth), Some(radius), Some(inner)) == 0 { + return None; + } + cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup + let edgeDir = if inner { + Direction::Left + } else { + Direction::Right + }; + + let mut neighbourMask = 0; + let start = cur.p(); + let mut sum = Point::default(); + let mut n = 0; + loop { + // log(cur.p, 4); + sum += cur.p().centered(); + n += 1; + + // find out if we come full circle around the center. 8 bits have to be set in the end. + neighbourMask |= 1 + << (4.0 + + Point::dot( + Point::floor(Point::bresenhamDirection(cur.p() - center)), + point(1.0, 3.0), + )) as u32; + + if !cur.stepAlongEdge(edgeDir, None) { + return None; + } + + // use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate + if Point::maxAbsComponent(cur.p - center) > radius as f32 + || center == cur.p + || n > 4 * 2 * range + { + return None; + } + + if !(cur.p != start) { + break; + } + } //while (cur.p != start); + + if requireCircle && neighbourMask != 0b111101111 { + return None; + } + + Some(sum / n as f32) +} + +pub fn CenterOfRings( + image: &BitMatrix, + center: Point, + range: i32, + numOfRings: u32, +) -> Option { + let mut n = 1; + let mut sum = center; + for i in 2..(numOfRings + 1) { + // for (int i = 1; i < numOfRings; ++i) { + let c = CenterOfRing(image, center.floor(), range, i as i32, true)?; + + if (c == Point::default()) { + if n == 1 { + return None; + } else { + return Some(sum / n as f32); + } + } else if Point::distance(c, center) > range as f32 / numOfRings as f32 / 2.0 { + return None; + } + + sum += c; + n += 1; + } + Some(sum / n as f32) +} + +pub fn CollectRingPoints( + image: &BitMatrix, + center: Point, + range: i32, + edgeIndex: i32, + backup: bool, +) -> Vec { + let centerI = center.floor(); + let radius = range; + let mut cur = EdgeTracer::new(image, centerI, point(0.0, 1.0)); + if cur.stepToEdge(Some(edgeIndex), Some(radius), Some(backup)) == 0 { + return Vec::default(); + } + cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup + let edgeDir = if backup { + Direction::Left + } else { + Direction::Right + }; + + let mut neighbourMask = 0; + let start = cur.p(); + let mut points = Vec::::with_capacity(4 * range as usize); + + loop { + // log(cur.p, 4); + points.push(cur.p().centered()); + + // find out if we come full circle around the center. 8 bits have to be set in the end. + neighbourMask |= 1 + << (4.0 + + Point::dot( + Point::round(Point::bresenhamDirection(cur.p - centerI)), + point(1.0, 3.0), + )) as u32; + + if !cur.stepAlongEdge(edgeDir, None) { + return Vec::default(); + } + + // use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate + if Point::maxAbsComponent(cur.p - centerI) > radius as f32 + || centerI == cur.p + || (points).len() > 4 * 2 * range as usize + { + return Vec::default(); + } + + if !(cur.p != start) { + break; + } + } //while (cur.p != start); + + if neighbourMask != 0b111101111 { + return Vec::default(); + } + + points +} + +pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option { + let dist2Center = |a, b| Point::distance(a, center) < Point::distance(b, center); + // rotate points such that the first one is the furthest away from the center (hence, a corner) + + let max_by_pred = |a: &&Point, b: &&Point| { + let da = Point::distance(**a, center); + let db = Point::distance(**b, center); + da.partial_cmp(&db).unwrap() + // if dist2Center(**a, **b) { + // std::cmp::Ordering::Greater + // } else { + // std::cmp::Ordering::Less + // } + }; + + let max = points.iter().max_by(max_by_pred)?; + + let pos = points.iter().position(|e| e == max)?; + + points.rotate_left(pos); + // std::rotate(points.begin(), std::max_element(points.begin(), points.end(), dist2Center), points.end()); + + let mut corners = [Point::default(); 4]; + corners[0] = points[0]; + // find the oposite corner by looking for the farthest point near the oposite point + corners[2] = *points[(points.len() * 3 / 8)..=(points.len() * 5 / 8)] + .iter() + .max_by(max_by_pred)?; + // corners[2] = std::max_element(&points[Size(points) * 3 / 8], &points[Size(points) * 5 / 8], dist2Center); + // find the two in between corners by looking for the points farthest from the long diagonal + let l = RegressionLine::with_two_points(corners[0], corners[2]); + let dist2Diagonal = /*[l = RegressionLine(*corners[0], *corners[2])]*/| a, b| { l.distance_single(a) < l.distance_single(b) }; + + let diagonal_max_by_pred = |p1: &Point, p2: &Point| { + let d1 = l.distance_single(*p1); + let d2 = l.distance_single(*p2); + d1.partial_cmp(&d2).unwrap() + // if dist2Diagonal(*p1, *p2) { + // std::cmp::Ordering::Greater + // } else { + // std::cmp::Ordering::Less + // } + }; + corners[1] = points[(points.len() / 8)..=(points.len() * 3 / 8)] + .iter() + .copied() + .max_by(diagonal_max_by_pred)?; + // corners[1] = std::max_element(&points[Size(points) * 1 / 8], &points[Size(points) * 3 / 8], dist2Diagonal); + corners[3] = points[(points.len() * 5 / 8)..=(points.len() * 7 / 8)] + .iter() + .copied() + .max_by(diagonal_max_by_pred)?; + // corners[3] = std::max_element(&points[Size(points) * 5 / 8], &points[Size(points) * 7 / 8], dist2Diagonal); + + let corner_positions = [ + 0, + points.iter().position(|p| *p == corners[1])?, + points.iter().position(|p| *p == corners[2])?, + points.iter().position(|p| *p == corners[3])?, + ]; + + let try_get_range = |a: usize, b: usize| -> Option<&[Point]> { + if a > b { + None + } else { + Some(&points[a + 1..b]) + } + }; + + let lines = [ + RegressionLine::with_point_slice(try_get_range(corner_positions[0], corner_positions[1])?), + RegressionLine::with_point_slice(try_get_range(corner_positions[1], corner_positions[2])?), + RegressionLine::with_point_slice(try_get_range(corner_positions[2], corner_positions[3])?), + RegressionLine::with_point_slice(try_get_range(corner_positions[3], points.len())?), + ]; + + // let lines = [ + // RegressionLine::with_point_slice(&points[corner_positions[0] + 1..corner_positions[1]]), + // RegressionLine::with_point_slice(&points[corner_positions[1] + 1..corner_positions[2]]), + // RegressionLine::with_point_slice(&points[corner_positions[2] + 1..corner_positions[3]]), + // RegressionLine::with_point_slice(&points[corner_positions[3] + 1..points.len()]), + // ]; + // std::array lines{RegressionLine{corners[0] + 1, corners[1]}, RegressionLine{corners[1] + 1, corners[2]}, + // RegressionLine{corners[2] + 1, corners[3]}, RegressionLine{corners[3] + 1, &points.back() + 1}}; + if lines.iter().any(|line| !line.isValid()) { + return None; + } + + let beg: [usize; 4] = [ + corner_positions[0] + 1, + corner_positions[1] + 1, + corner_positions[2] + 1, + corner_positions[3] + 1, + ]; + let end: [usize; 4] = [ + corner_positions[1], + corner_positions[2], + corner_positions[3], + points.len(), + ]; + + // check if all points belonging to each line segment are sufficiently close to that line + for i in 0..4 { + // for (int i = 0; i < 4; ++i){ + for p in &points[beg[i]..end[i]] { + // for (const PointF* p = beg[i]; p != end[i]; ++p) { + let len = (end[i] - beg[i]) as f64; //std::distance(beg[i], end[i]); + if (len > 3.0 + && (lines[i].distance_single(*p) as f64) > f64::max(1.0, f64::min(8.0, len / 8.0))) + { + // #ifdef PRINT_DEBUG + // printf("%d: %.2f > %.2f @ %.fx%.f\n", i, lines[i].distance(*p), std::distance(beg[i], end[i]) / 1., p->x, p->y); + // #endif + return None; + } + } + } + + let mut res = Quadrilateral::default(); + for i in 0..4 { + // for (int i = 0; i < 4; ++i) { + res[i] = RegressionLine::intersect(&lines[i], &lines[(i + 1) % 4])?; + } + + Some(res) +} + +pub fn QuadrilateralIsPlausibleSquare(q: &Quadrilateral, lineIndex: usize) -> bool { + let mut m; + + m = Point::distance(q[0], q[3]) as f64; //M = distance(q[0], q[3]); + let mut M = m; + + for i in 1..4 { + // for (int i = 1; i < 4; ++i) + + UpdateMinMaxFloat(&mut m, &mut M, Point::distance(q[i - 1], q[i]) as f64); + } + + m >= (lineIndex * 2) as f64 && m > M / 3.0 +} + +pub fn FitSquareToPoints( + image: &BitMatrix, + center: Point, + range: i32, + lineIndex: i32, + backup: bool, +) -> Option { + let mut points = CollectRingPoints(image, center, range, lineIndex, backup); + if points.is_empty() { + return None; + } + + let res = FitQadrilateralToPoints(center, &mut points)?; + if !QuadrilateralIsPlausibleSquare(&res, (lineIndex - i32::from(backup)) as usize) { + return None; + } + + Some(res) +} + +pub fn FindConcentricPatternCorners( + image: &BitMatrix, + center: Point, + range: i32, + lineIndex: i32, +) -> Option { + let innerCorners = FitSquareToPoints(image, center, range, lineIndex, false)?; + + let outerCorners = FitSquareToPoints(image, center, range, lineIndex + 1, true)?; + + let res = Quadrilateral::blend(&innerCorners, &outerCorners); + + // for p in innerCorners{ + // log(p, 3);} + + // for p in outerCorners{ + // log(p, 3);} + + // for p in res{ + // log(p, 3);} + + Some(res) +} + +#[derive(Default, Copy, Clone, Eq, PartialEq, Debug)] +pub struct ConcentricPattern { + pub p: Point, + pub size: i32, +} + +impl std::ops::Sub for ConcentricPattern { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + let new_p = self.p - rhs.p; + Self { + p: new_p, + size: self.size, + } + } +} + +impl std::ops::Add for ConcentricPattern { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + let new_p = self.p + rhs.p; + Self { + p: new_p, + size: self.size, + } + } +} + +impl From for ConcentricPattern { + fn from(value: Point) -> Self { + Self { p: value, size: 0 } + } +} + +impl ConcentricPattern { + pub fn dot(self, other: ConcentricPattern) -> f32 { + Point::dot(self.p, other.p) + } + + pub fn cross(self, other: ConcentricPattern) -> f32 { + Point::cross(self.p, other.p) + } + + pub fn distance(self, other: ConcentricPattern) -> f32 { + Point::distance(self.p, other.p) + } +} + +pub fn LocateConcentricPattern( + image: &BitMatrix, + pattern: &Pattern, + center: Point, + range: i32, +) -> Option { + let mut cur = EdgeTracer::new(image, center.floor(), Point::default()); + let mut minSpread = image.getWidth() as i32; + let mut maxSpread = 0_i32; + + // TODO: setting maxError to 1 can subtantially help with detecting symbols with low print quality resulting in damaged + // finder patterns, but it sutantially increases the runtime (approx. 20% slower for the falsepositive images). + let mut maxError = 0; + for d in [point(0.0, 1.0), point(1.0, 0.0)] { + // for (auto d : {PointI{0, 1}, {1, 0}}) { + cur.setDirection(d); // THIS COULD POSSIBLY BE WRONG, WE MIGHT MEAN TO CLONE cur EACH RUN? + + let spread = CheckSymmetricPattern::(&mut cur, pattern, range, true); + if spread != 0 { + UpdateMinMax(&mut minSpread, &mut maxSpread, spread); + } else { + maxError -= 1; + if maxError < 0 { + return None; + } + } + } + + //#if 1 + for d in [point(1.0, 1.0), point(1.0, -1.0)] { + // for (auto d : {PointI{1, 1}, {1, -1}}) { + cur.setDirection(d); // THIS COULD POSSIBLY BE WRONG, WE MIGHT MEAN TO CLONE cur EACH RUN? + let spread = CheckSymmetricPattern::(&mut cur, pattern, range * 2, false); + if spread != 0 { + UpdateMinMax(&mut minSpread, &mut maxSpread, spread); + } else { + maxError -= 1; + if maxError < 0 { + return None; + } + } + } + //#endif + + if maxSpread > 5 * minSpread { + return None; + } + + let newCenter = FinetuneConcentricPatternCenter(image, cur.p(), range, pattern.len() as u32)?; + + Some(ConcentricPattern { + p: newCenter, + size: (maxSpread + minSpread) / 2, + }) +} + +pub fn FinetuneConcentricPatternCenter( + image: &BitMatrix, + center: Point, + range: i32, + finderPatternSize: u32, +) -> Option { + // make sure we have at least one path of white around the center + if let Some(res1) = CenterOfRing(image, center.floor(), range, 1, true) { + if !image.get_point(res1) { + return None; + } + // and then either at least one more ring around that + if let Some(res2) = CenterOfRings(image, res1, range, finderPatternSize / 2) { + return Some(res2); + } + // or the center can be approximated by a square + if (FitSquareToPoints(image, res1, range, 1, false).is_some()) { + return Some(res1); + } + // TODO: this is currently only keeping #258 alive, evaluate if still worth it + if let Some(res2) = + CenterOfDoubleCross(image, res1.floor(), range, finderPatternSize / 2 + 1) + { + return Some(res2); + } + } + None + + // // make sure we have at least one path of white around the center + // let res = CenterOfRing(image, center, range, 1, false)?; + + // let center = res; + + // let mut res = CenterOfRings(image, center, range, finderPatternSize / 2); + + // if res.is_none() || !image.get_point(res?) { + // res = CenterOfDoubleCross(image, center, range, finderPatternSize / 2 + 1); + // } + // if res.is_none() || !image.get_point(res?) { + // res = Some(center); + // } + // if res.is_none() || !image.get_point(res?) { + // return None; + // } + + // res +} diff --git a/src/common/cpp_essentials/decoder_result.rs b/src/common/cpp_essentials/decoder_result.rs new file mode 100644 index 0000000..04a3f66 --- /dev/null +++ b/src/common/cpp_essentials/decoder_result.rs @@ -0,0 +1,192 @@ +use std::{any::Any, rc::Rc}; + +use crate::{common::ECIStringBuilder, Exceptions, RXingResult}; + +use super::StructuredAppendInfo; + +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct DecoderResult +where + T: Copy + Clone + Default + Eq + PartialEq, +{ + content: ECIStringBuilder, + ecLevel: String, + lineCount: u32, // = 0; + versionNumber: u32, // = 0; + structuredAppend: StructuredAppendInfo, + isMirrored: bool, // = false; + readerInit: bool, // = false; + //Error _error; + //std::shared_ptr _extra; + error: Option, + extra: Rc, +} + +impl Default for DecoderResult +where + T: Copy + Clone + Default + Eq + PartialEq, +{ + fn default() -> Self { + Self { + content: Default::default(), + ecLevel: Default::default(), + lineCount: 0, + versionNumber: 0, + structuredAppend: Default::default(), + isMirrored: false, + readerInit: false, + error: None, + extra: Default::default(), + } + } +} + +impl DecoderResult +where + T: Copy + Clone + Default + Eq + PartialEq, +{ + pub fn new() -> Self { + Self::default() + } + pub fn with_eci_string_builder(src: ECIStringBuilder) -> Self { + let mut new_self = Self::default(); + new_self.content = src; + + new_self + } + + pub fn isValid(&self) -> bool { + self.content.symbology.code != 0 && self.error.is_none() + //return includeErrors || (_content.symbology.code != 0 && !_error); + } + + pub fn content(&self) -> &ECIStringBuilder { + &self.content + } +} + +impl DecoderResult +where + T: Copy + Clone + Default + Eq + PartialEq, +{ + pub fn ecLevel(&self) -> &str { + &self.ecLevel + } + pub fn setEcLevel(&mut self, ecLevel: String) { + self.ecLevel = ecLevel + } + pub fn withEcLevel(mut self, ecLevel: String) -> DecoderResult { + self.setEcLevel(ecLevel); + self + } + + pub fn lineCount(&self) -> u32 { + self.lineCount + } + pub fn setLineCount(&mut self, lc: u32) { + self.lineCount = lc + } + pub fn withLineCount(mut self, lc: u32) -> DecoderResult { + self.setLineCount(lc); + self + } + + pub fn versionNumber(&self) -> u32 { + self.versionNumber + } + pub fn setVersionNumber(&mut self, vn: u32) { + self.versionNumber = vn + } + pub fn withVersionNumber(mut self, vn: u32) -> DecoderResult { + self.setVersionNumber(vn); + self + } + + pub fn structuredAppend(&self) -> &StructuredAppendInfo { + &self.structuredAppend + } + pub fn setStructuredAppend(&mut self, sai: StructuredAppendInfo) { + self.structuredAppend = sai + } + pub fn withStructuredAppend(mut self, sai: StructuredAppendInfo) -> DecoderResult { + self.setStructuredAppend(sai); + self + } + + pub fn isMirrored(&self) -> bool { + self.isMirrored + } + pub fn setIsMirrored(&mut self, is_mirrored: bool) { + self.isMirrored = is_mirrored + } + pub fn withIsMirrored(mut self, is_mirrored: bool) -> DecoderResult { + self.setIsMirrored(is_mirrored); + self + } + + pub fn readerInit(&self) -> bool { + self.readerInit + } + pub fn setReaderInit(&mut self, reader_init: bool) { + self.readerInit = reader_init + } + pub fn withReaderInit(mut self, reader_init: bool) -> DecoderResult { + self.setReaderInit(reader_init); + self + } + + pub fn extra(&self) -> Rc { + self.extra.clone() + } + pub fn setExtra(&mut self, extra: Rc) { + self.extra = extra + } + pub fn withExtra(mut self, extra: Rc) -> DecoderResult { + self.setExtra(extra); + self + } + + pub fn error(&self) -> &Option { + &self.error + } + pub fn setError(&mut self, error: Option) { + self.error = error + } + pub fn withError(mut self, error: Option) -> DecoderResult { + self.setError(error); + self + } + + // pub fn build(self) -> DecoderResult { + + // } +} + +impl DecoderResult +where + T: Copy + Clone + Default + Eq + PartialEq, +{ + pub fn text(&self) -> String { + self.content.to_string() + } + + pub fn symbologyIdentifier(&self) -> String { + let s = self.content.symbology; + if s.code > 0 { + format!( + "]{}{}", + char::from(s.code), + char::from( + s.modifier + + if self.content.has_eci { + s.eciModifierOffset + } else { + 0 + } + ) + ) + } else { + String::default() + } + } +} diff --git a/src/datamatrix/detector/zxing_cpp_detector/direction.rs b/src/common/cpp_essentials/direction.rs similarity index 100% rename from src/datamatrix/detector/zxing_cpp_detector/direction.rs rename to src/common/cpp_essentials/direction.rs diff --git a/src/datamatrix/detector/zxing_cpp_detector/dm_regression_line.rs b/src/common/cpp_essentials/dm_regression_line.rs similarity index 94% rename from src/datamatrix/detector/zxing_cpp_detector/dm_regression_line.rs rename to src/common/cpp_essentials/dm_regression_line.rs index d6e8d9e..74f4a8b 100644 --- a/src/datamatrix/detector/zxing_cpp_detector/dm_regression_line.rs +++ b/src/common/cpp_essentials/dm_regression_line.rs @@ -1,10 +1,7 @@ use crate::common::Result; use crate::{Exceptions, Point}; -use super::{ - util::{float_max, float_min}, - RegressionLine, -}; +use super::RegressionLineTrait; #[derive(Clone)] pub struct DMRegressionLine { @@ -30,7 +27,7 @@ impl Default for DMRegressionLine { } } -impl RegressionLine for DMRegressionLine { +impl RegressionLineTrait for DMRegressionLine { fn points(&self) -> &[Point] { &self.points } @@ -136,14 +133,14 @@ impl RegressionLine for DMRegressionLine { let Some(mut min) = self.points.first().copied() else { return false }; let Some(mut max) = self.points.first().copied() else { return false }; for p in &self.points { - min.x = float_min(min.x, p.x); - min.y = float_min(min.y, p.y); - max.x = float_max(max.x, p.x); - max.y = float_max(max.y, p.y); + min.x = f32::min(min.x, p.x); + min.y = f32::min(min.y, p.y); + max.x = f32::max(max.x, p.x); + max.y = f32::max(max.y, p.y); } let diff = max - min; let len = diff.maxAbsComponent(); - let steps = float_min(diff.x.abs(), diff.y.abs()); + let steps = f32::min(diff.x.abs(), diff.y.abs()); // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal steps > 2.0 || len > 50.0 } @@ -211,9 +208,27 @@ impl RegressionLine for DMRegressionLine { Point::dot(self.direction_inward, self.normal()) > 0.5 // angle between original and new direction is at most 60 degree } + + fn a(&self) -> f32 { + self.a + } + + fn b(&self) -> f32 { + self.b + } + + fn c(&self) -> f32 { + self.c + } } impl DMRegressionLine { + pub fn new(point_1: Point, point_2: Point) -> Self { + let mut new = Self::default(); + RegressionLineTrait::evaluate(&mut new, &[point_1, point_2]); + new + } + // template fn average(c: &[f64], f: T) -> f64 where diff --git a/src/datamatrix/detector/zxing_cpp_detector/edge_tracer.rs b/src/common/cpp_essentials/edge_tracer.rs similarity index 92% rename from src/datamatrix/detector/zxing_cpp_detector/edge_tracer.rs rename to src/common/cpp_essentials/edge_tracer.rs index 186aa81..309d7c7 100644 --- a/src/datamatrix/detector/zxing_cpp_detector/edge_tracer.rs +++ b/src/common/cpp_essentials/edge_tracer.rs @@ -6,13 +6,13 @@ use crate::{ Exceptions, Point, }; -use super::{BitMatrixCursor, Direction, RegressionLine, StepResult, Value}; +use super::{BitMatrixCursorTrait, Direction, RegressionLineTrait, StepResult, Value}; #[derive(Clone)] pub struct EdgeTracer<'a> { - pub(super) img: &'a BitMatrix, + pub(crate) img: &'a BitMatrix, - pub(super) p: Point, // current position + pub(crate) p: Point, // current position d: Point, // current direction // pub history: Option<&'a mut ByteMatrix>, // = nullptr; @@ -34,7 +34,7 @@ pub struct EdgeTracer<'a> { // } // } -impl BitMatrixCursor for EdgeTracer<'_> { +impl BitMatrixCursorTrait for EdgeTracer<'_> { fn testAt(&self, p: Point) -> Value { if self.img.isIn(p, 0) { Value::from(self.img.get_point(p)) @@ -119,13 +119,20 @@ impl BitMatrixCursor for EdgeTracer<'_> { self.isIn(self.p) } - fn movedBy(self, d: Point) -> Self { + fn movedBy(self, d: Point) -> Self { let mut res = self; res.p += d; res } + fn turnedBack(&self) -> Self { + let mut res = self.clone(); + res.d = res.back(); + + res + } + /** * @brief stepToEdge advances cursor to one step behind the next (or n-th) edge. * @param nth number of edges to pass @@ -134,10 +141,10 @@ impl BitMatrixCursor for EdgeTracer<'_> { * @return number of steps taken or 0 if moved outside of range/image */ fn stepToEdge(&mut self, nth: Option, range: Option, backup: Option) -> i32 { - let mut nth = if let Some(nth) = nth { nth } else { 1 }; - let range = if let Some(r) = range { r } else { 0 }; - let backup = if let Some(b) = backup { b } else { false }; - // TODO: provide an alternative and faster out-of-bounds check than isIn() inside testAt() + let mut nth = nth.unwrap_or(1); //if let Some(nth) = nth { nth } else { 1 }; + let range = range.unwrap_or(0); //if let Some(r) = range { r } else { 0 }; + let backup = backup.unwrap_or(false); //if let Some(b) = backup { b } else { false }; + // TODO: provide an alternative and faster out-of-bounds check than isIn() inside testAt() let mut steps = 0; let mut lv = self.testAt(self.p); @@ -155,6 +162,18 @@ impl BitMatrixCursor for EdgeTracer<'_> { self.p += self.d * steps; steps * i32::from(nth == 0) } + + fn p(&self) -> Point { + self.p + } + + fn d(&self) -> Point { + self.d + } + + fn img(&self) -> &BitMatrix { + self.img + } } impl<'a> EdgeTracer<'_> { @@ -163,7 +182,7 @@ impl<'a> EdgeTracer<'_> { EdgeTracer { img: image, p, - d, + d: Point::bresenhamDirection(d), //d, history: None, state: 0, } @@ -259,7 +278,11 @@ impl<'a> EdgeTracer<'_> { true } - pub fn traceLine(&mut self, dEdge: Point, line: &mut T) -> Result { + pub fn traceLine( + &mut self, + dEdge: Point, + line: &mut T, + ) -> Result { line.setDirectionInward(dEdge); loop { // log(self.p); @@ -286,7 +309,7 @@ impl<'a> EdgeTracer<'_> { } // while (true); } - pub fn traceGaps( + pub fn traceGaps( &mut self, dEdge: Point, line: &mut T, diff --git a/src/common/cpp_essentials/fast_edge_to_edge_counter.rs b/src/common/cpp_essentials/fast_edge_to_edge_counter.rs new file mode 100644 index 0000000..d28729c --- /dev/null +++ b/src/common/cpp_essentials/fast_edge_to_edge_counter.rs @@ -0,0 +1,84 @@ +use crate::common::BitMatrix; + +use super::BitMatrixCursorTrait; + +pub struct FastEdgeToEdgeCounter<'a> { + // const uint8_t* p = nullptr; + // int stride = 0; + // int stepsToBorder = 0; + p: u32, // = nullptr; + stride: isize, // = 0; + stepsToBorder: i32, // = 0; + //arr: BitArray, + _arr: isize, + under_arry: &'a BitMatrix, //,Vec +} + +impl<'a> FastEdgeToEdgeCounter<'a> { + pub fn new(cur: &T) -> FastEdgeToEdgeCounter { + let stride = cur.d().y as isize * cur.img().width() as isize + cur.d().x as isize; + let p = ((cur.p().y as isize * cur.img().width() as isize).abs() as i32 + cur.p().x as i32) + as u32; // P IS SET WRONG IN REVERSE + + let maxStepsX: i32 = if cur.d().x != 0.0 { + if cur.d().x > 0.0 { + cur.img().width() as i32 - 1 - cur.p().x as i32 + } else { + cur.p().x as i32 + } + } else { + i32::MAX + }; + let maxStepsY: i32 = if cur.d().y != 0.0 { + if cur.d().y > 0.0 { + cur.img().height() as i32 - 1 - cur.p().y as i32 + } else { + cur.p().y as i32 + } + } else { + i32::MAX + }; + let stepsToBorder = std::cmp::min(maxStepsX, maxStepsY) as i32; + + FastEdgeToEdgeCounter { + p, + stride, + stepsToBorder, + _arr: cur.p().y as isize * stride as isize, //cur.img().getRow(cur.p().y as u32), + under_arry: cur.img(), //.into(), + } + } + + pub fn stepToNextEdge(&mut self, range: u32) -> u32 { + let maxSteps = std::cmp::min(self.stepsToBorder, range as i32); + let mut steps = 0; + loop { + steps += 1; + if steps > maxSteps { + if maxSteps == self.stepsToBorder { + break; + } else { + return 0; + } + } + + let idx_pt = self.get_array_check_index(steps); + + // if !(self.under_arry[idx_pt] + // == self.under_arry[self.p as usize]) + if !(self.under_arry.get_index(idx_pt) == self.under_arry.get_index(self.p as usize)) { + break; + } + } // while (p[steps * stride] == p[0]); + + self.p = (self.p as isize + (steps as isize * self.stride)).abs() as u32; + self.stepsToBorder -= steps; + + return steps as u32; + } + + #[inline(always)] + fn get_array_check_index(&self, steps: i32) -> usize { + (self.p as isize + (steps as isize * self.stride)) as usize + } +} diff --git a/src/common/cpp_essentials/matrix.rs b/src/common/cpp_essentials/matrix.rs new file mode 100644 index 0000000..b788e74 --- /dev/null +++ b/src/common/cpp_essentials/matrix.rs @@ -0,0 +1,108 @@ +use crate::common::Result; +use crate::{Exceptions, Point}; + +#[derive(Default, Clone, PartialEq, Eq)] +pub struct Matrix { + width: usize, + height: usize, + data: Vec>, +} + +impl Matrix { + pub fn with_data(width: usize, height: usize, data: Vec>) -> Result> { + if width != 0 && data.len() / width as usize != height as usize { + return Err(Exceptions::illegal_argument_with( + "invalid size: width * height is too big", + )); + } + Ok(Self { + width, + height, + data, + }) + } + + pub fn new(width: usize, height: usize) -> Result> { + if (width != 0 && (width * height) / width as usize != height as usize) { + return Err(Exceptions::illegal_argument_with( + "invalid size: width * height is too big", + )); + } + Ok(Self { + width, + height, + data: vec![None; width * height], + }) + } + + pub fn height(&self) -> usize { + self.height + } + + pub fn width(&self) -> usize { + self.width + } + + pub fn size(&self) -> usize { + self.data.len() + } + + // value_t& operator()(int x, int y) + // { + // assert(x >= 0 && x < _width && y >= 0 && y < _height); + // return _data[y * _width + x]; + // } + + // const T& operator()(int x, int y) const + // { + // assert(x >= 0 && x < _width && y >= 0 && y < _height); + // return _data[y * _width + x]; + // } + + fn get_offset(x: usize, y: usize, width: usize) -> usize { + (y * width + x) as usize + } + + pub fn get(&self, x: usize, y: usize) -> Option { + if x >= self.width || y >= self.height { + None + } else if let Some(Some(d)) = self.data.get(Self::get_offset(x, y, self.width)) { + Some(*d) + } else { + None + } + } + + pub fn set(&mut self, x: usize, y: usize, value: T) -> T { + self.data[Self::get_offset(x, y, self.width)] = Some(value); + self.get(x, y).unwrap() + } + + pub fn get_point(&self, p: Point) -> Option { + self.get(p.x as usize, p.y as usize) + } + + pub fn set_point(&mut self, p: Point, value: T) -> T { + self.set(p.x as usize, p.y as usize, value) + } + + pub fn data(&self) -> &[Option] { + &self.data + } + + // const value_t* begin() const { + // return _data.data(); + // } + + // const value_t* end() const { + // return _data.data() + _width * _height; + // } + + pub fn clear_with(&mut self, value: T) { + self.data.fill(Some(value)) + } + + pub fn clear(&mut self) { + self.data.fill(None) + } +} diff --git a/src/common/cpp_essentials/mod.rs b/src/common/cpp_essentials/mod.rs new file mode 100644 index 0000000..07120a8 --- /dev/null +++ b/src/common/cpp_essentials/mod.rs @@ -0,0 +1,35 @@ +mod base_extentions; + +pub mod bitmatrix_cursor; +pub mod bitmatrix_cursor_trait; +pub mod concentric_finder; +pub mod decoder_result; +pub mod direction; +pub mod dm_regression_line; +pub mod edge_tracer; +pub mod fast_edge_to_edge_counter; +pub mod matrix; +pub mod pattern; +pub mod regression_line; +pub mod regression_line_trait; +pub mod step_result; +pub mod structured_append; +pub mod util; +pub mod value; + +pub use bitmatrix_cursor::*; +pub use bitmatrix_cursor_trait::*; +pub use concentric_finder::*; +pub use decoder_result::*; +pub use direction::*; +pub use dm_regression_line::*; +pub use edge_tracer::*; +pub use fast_edge_to_edge_counter::*; +pub use matrix::*; +pub use pattern::*; +pub use regression_line::*; +pub use regression_line_trait::*; +pub use step_result::*; +pub use structured_append::*; +pub use util::*; +pub use value::*; diff --git a/src/common/cpp_essentials/pattern.rs b/src/common/cpp_essentials/pattern.rs new file mode 100644 index 0000000..f04b873 --- /dev/null +++ b/src/common/cpp_essentials/pattern.rs @@ -0,0 +1,855 @@ +/* +* Copyright 2020 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{BitMatrix, Result}, + Exceptions, +}; + +pub type PatternType = u16; +pub type Pattern = [PatternType; N]; + +fn BarAndSpaceSum< + const LEN: usize, + T: Into + Copy, + RT: Default + std::cmp::PartialEq + std::ops::AddAssign, +>( + view: &[T], +) -> BarAndSpace { + let mut res = BarAndSpace::default(); + for i in 0..LEN { + // for (int i = 0; i < LEN; ++i) + res[i] += view[i].into(); + } + res +} + +#[derive(Default, Debug)] +pub struct PatternRow(Vec); + +// pub struct PatternRow + Into + Copy>(Vec); + +impl PatternRow { + pub fn new(v: Vec) -> Self { + Self(v) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn into_pattern_view(&self) -> PatternView { + PatternView::new(self) + } +} + +impl IntoIterator for PatternRow { + type Item = PatternType; + + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl std::ops::Index for PatternRow { + type Output = PatternType; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl std::ops::IndexMut for PatternRow { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } +} + +impl From> for PatternRow { + fn from(value: Vec) -> Self { + Self(value) + } +} + +pub struct PatternViewIterator<'a> { + pattern_view: &'a PatternView<'a>, + current_position: usize, +} + +impl<'a> Iterator for PatternViewIterator<'_> { + type Item = PatternType; + + fn next(&mut self) -> Option { + if self.current_position + 1 > self.pattern_view.count { + return None; + } + + self.current_position += 1; + + Some( + *self.pattern_view.data.0.get( + self.current_position - 1 + self.pattern_view.start + self.pattern_view.current, + )?, + ) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PatternView<'a> { + data: &'a PatternRow, + start: usize, + count: usize, + current: usize, +} + +impl<'a> PatternView<'a> { + // A PatternRow always starts with the width of whitespace in front of the first black bar. + // The first element of the PatternView is the first bar. + pub fn new(bars: &'a PatternRow) -> PatternView<'a> { + PatternView { + data: bars, + start: 1, + count: bars.0.len(), + current: 0, + } + } + + pub fn with_config( + bars: &'a PatternRow, + start: usize, + size: usize, + base: usize, + _end: usize, + ) -> PatternView<'a> { + PatternView { + data: bars, + start, + count: size, + current: base, + } + } + + pub fn data(&self) -> &PatternRow { + self.data + } + pub fn begin(&self) -> Option { + Some(*self.data.0.get(self.start)?) + } + pub fn end(&self) -> Option { + // if self.start + self.count < self.data.0.len() { + // Some(self.data.0[self.start + self.count]) + // } else { + // None + // } + Some(self.data.0.len() as PatternType) + } + + // int sum(int n = 0) const { return std::accumulate(_data, _data + (n == 0 ? _size : n), 0); } + pub fn sum(&self, n: Option) -> PatternType { + if self.count == self.data.len() { + return self.data.0.iter().sum::(); + } + + let n = n.unwrap_or(self.count); + + self.data + .0 + .iter() + .skip(self.start + self.current) + .take(n) + .copied() + .sum::() + } + + pub fn iter(&'a self) -> PatternViewIterator<'a> { + PatternViewIterator { + pattern_view: self, + current_position: 0, + } + } + + pub fn size(&self) -> usize { + self.count + } + + // index is the number of bars and spaces from the first bar to the current position + pub fn index(&self) -> usize { + self.current /*return narrow_cast(_data - _base) - 1;*/ + } + pub fn pixelsInFront(&self) -> PatternType { + self.data + .0 + .iter() + .take(self.start + self.current) + .copied() + .sum::() /*return std::accumulate(_base, _data, 0);*/ + } + pub fn pixelsTillEnd(&self) -> PatternType { + self.data + .0 + .iter() + .skip(self.start + self.current) + .copied() + .sum::() /*return std::accumulate(_base, _data + _size, 0) - 1;*/ + } + pub fn isAtFirstBar(&self) -> bool { + self.start == (self.current + 1) /*return _data == _base + 1;*/ + } + pub fn isAtLastBar(&self) -> bool { + self.current == self.start + self.count - 1 /*return _data + _size == _end - 1;*/ + } + 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()) + /*return _data && _data >= _base && _data + n <= _end;*/ + } + pub fn isValid(&self) -> bool { + self.isValidWithN(self.size()) + } + + pub fn has_quiet_zone_before(&self, scale: f32, acceptIfAtFirstBar: Option) -> bool { + (acceptIfAtFirstBar.unwrap_or(false) && self.isAtLastBar()) + || Into::::into(self.data.0[self.count]) + >= Into::::into(self.sum(None)) * scale + } + // template + // bool hasQuietZoneBefore(float scale) const + // { + // return (acceptIfAtFirstBar && isAtFirstBar()) || _data[-1] >= sum() * scale; + // } + + pub fn hasQuietZoneAfter(&self, scale: f32, acceptIfAtLastBar: Option) -> bool { + (acceptIfAtLastBar.unwrap_or(true) && self.isAtLastBar()) + || Into::::into(self.data.0[self.count]) + >= Into::::into(self.sum(None)) * scale + } + + // template + // bool hasQuietZoneAfter(float scale) const + // { + // return (acceptIfAtLastBar && isAtLastBar()) || _data[_size] >= sum() * scale; + // } + + pub fn subView(&self, offset: usize, size: Option) -> PatternView<'a> { + let mut size = size.unwrap_or(0); + if size == 0 { + size = self.count - offset; + } else if size < 0 { + size += self.count - offset; + } + + PatternView { + data: self.data, + start: self.start + offset, + count: size, + current: self.current, + } + } + + // PatternView subView(int offset, int size = 0) const + // { + // // if(std::abs(size) > count()) + // // printf("%d > %d\n", std::abs(size), _count); + // // assert(std::abs(size) <= count()); + // if (size == 0) + // size = _size - offset; + // else if (size < 0) + // size = _size - offset + size; + // return {begin() + offset, std::max(size, 0), _base, _end}; + // } + + pub fn shift(&mut self, n: usize) -> bool { + self.current += n; + !self.data.0.is_empty() && self.start + self.count <= (self.start + self.count) + } + + // bool shift(int n) + // { + // return _data && ((_data += n) + _size <= _end); + // } + + pub fn skipPair(&mut self) -> bool { + self.shift(2) + } + + pub fn skipSymbol(&mut self) -> bool { + self.shift(self.count) + } + + pub fn skipSingle(&mut self /* maxWidth: usize */) -> bool { + self.shift(1) //&& _data[-1] <= maxWidth; + } + + pub fn extend(&mut self) { + self.count = std::cmp::max(0, self.data.len() - (self.current + self.start)) + } + + fn try_get_index(&self, index: isize) -> Option { + if index.abs() > self.data.0.len() as isize { + return None; + } + if index >= 0 { + let fetch_spot = ((self.start + self.current) as isize + index) as usize; + return Some(self.data.0[fetch_spot]); + } + if index.abs() > (self.start + self.current) as isize { + return None; + } + let fetch_spot = ((self.start + self.current) as isize + index) as usize; + Some(self.data.0[fetch_spot]) + } +} + +impl<'a> std::ops::Index for PatternView<'_> { + type Output = PatternType; + + fn index(&self, index: isize) -> &Self::Output { + if self.count == self.data.len() { + return &self.data[index.abs() as usize]; + } + + if index > self.data.0.len() as isize { + panic!("array index out of bounds") + } + if index >= 0 { + let fetch_spot = ((self.start + self.current) as isize + index) as usize; + return &self.data.0[fetch_spot]; + } + if index.abs() > self.start as isize { + panic!("array index out of bounds") + } + let fetch_spot = ((self.start + self.current) as isize + index) as usize; + &self.data.0[fetch_spot] + } +} + +impl<'a> std::ops::Index for PatternView<'_> { + type Output = PatternType; + + fn index(&self, index: usize) -> &Self::Output { + if self.count == self.data.len() { + return &self.data[index]; + } + + if index > self.data.0.len() { + panic!("array index out of bounds") + } + self.data.0.get(self.start + self.current + index).unwrap() + } +} + +impl<'a> std::ops::Index for PatternView<'_> { + type Output = PatternType; + + fn index(&self, index: i32) -> &Self::Output { + std::ops::Index::::index(self, index as isize) + } +} + +impl<'a> Into> for &PatternView<'a> { + fn into(self) -> Vec { + let mut v = vec![PatternType::default(); self.count as usize]; + for i in 0..self.count { + v[i] = self[i]; + } + v + } +} + +/** + * @brief The BarAndSpace struct is a simple 2 element data structure to hold information about bar(s) and space(s). + * + * The operator[](int) can be used in combination with a PatternView + */ +#[derive(Default)] +struct BarAndSpace { + bar: T, + space: T, +} +impl BarAndSpace { + pub fn isValid(&self) -> bool { + self.bar != T::default() && self.space != T::default() + } +} + +impl std::ops::Index for BarAndSpace { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + match index & 1 { + 0 => &self.bar, + 1 => &self.space, + _ => panic!("Index out of range for BarAndSpace"), + } + } +} + +impl std::ops::IndexMut for BarAndSpace { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + match index & 1 { + 0 => &mut self.bar, + 1 => &mut self.space, + _ => panic!("Index out of range for BarAndSpace"), + } + } +} + +// using value_type = T; +// T bar = {}, space = {}; +// // even index -> bar, odd index -> space +// T& operator[](int i) { return reinterpret_cast(this)[i & 1]; } +// T operator[](int i) const { return reinterpret_cast(this)[i & 1]; } +// bool isValid() const { return bar != T{} && space != T{}; } +// }; + +type BarAndSpaceI = BarAndSpace; + +/** + * @brief FixedPattern describes a compile-time constant (start/stop) pattern. + * + * @param N number of bars/spaces + * @param SUM sum over all N elements (size of pattern in modules) + * @param IS_SPARCE whether or not the pattern contains '0's denoting 'wide' bars/spaces + */ +pub struct FixedPattern { + data: [PatternType; N], +} + +impl Into> + for FixedPattern +{ + fn into(self) -> Pattern { + self.data + } +} + +impl FixedPattern { + pub const fn new(data: [PatternType; N]) -> Self { + FixedPattern { data } + } + + pub fn with_reference(data: &[PatternType; N]) -> Self { + FixedPattern { data: *data } + } + + fn as_slice(&self) -> &[PatternType] { + &self.data + } + + fn size(&self) -> usize { + N + } + + fn sums(&self) -> BarAndSpace { + return BarAndSpaceSum::(&self.data); + } +} + +impl std::ops::Index + for FixedPattern +{ + type Output = PatternType; + + fn index(&self, index: usize) -> &Self::Output { + &self.data[index] + } +} + +pub type FixedSparcePattern = FixedPattern; + +// template +// struct FixedPattern +// { +// using value_type = PatternRow::value_type; +// value_type _data[N]; +// constexpr value_type operator[](int i) const noexcept { return _data[i]; } +// constexpr const value_type* data() const noexcept { return _data; } +// constexpr int size() const noexcept { return N; } +// }; + +// template +// using FixedSparcePattern = FixedPattern; + +pub fn IsPattern( + view: &PatternView, + pattern: &FixedPattern, + space_in_pixel: Option, + min_quiet_zone: f32, + module_size_ref: f32, + // e2e: Option, +) -> f32 { + //let e2e = E2E; //e2e.unwrap_or(false); + let mut module_size_ref = module_size_ref; + + if E2E { + //using float_t = double; + let v_src: Vec = view.into(); + let widths = BarAndSpaceSum::(&v_src); + let sums = pattern.sums(); + let modSize: BarAndSpace = BarAndSpace { + bar: widths[0] / sums[0] as f64, + space: widths[1] / sums[1] as f64, + }; + + let [m, M] = [ + f64::min(modSize[0], modSize[1]), + f64::max(modSize[0], modSize[1]), + ]; + if (M > 4.0 * m) { + // make sure module sizes of bars and spaces are not too far away from each other + return 0.0; + } + + if (min_quiet_zone != 0.0 + && (space_in_pixel.unwrap_or_default()) < min_quiet_zone * modSize.space as f32) + { + return 0.0; + } + + let thr: BarAndSpace = BarAndSpace { + bar: modSize[0] * 0.75 + 0.5, + space: modSize[1] / (2.0 + f64::from(LEN < 6)) + 0.5, + }; + + for x in 0..LEN { + // for (int x = 0; x < LEN; ++x){ + if (view[x] as f64 - pattern[x] as f64 * modSize[x]).abs() > thr[x] { + return 0.0; + } + } + + let moduleSize: f64 = (modSize[0] + modSize[1]) / 2.0; + return moduleSize as f32; + } + + let width = view.sum(Some(LEN)); + if SUM > LEN && Into::::into(width) < SUM { + return 0.0; + } + + let module_size: f32 = (Into::::into(width)) / (SUM as f32); + + if min_quiet_zone != 0.0 + && (space_in_pixel.unwrap_or(f32::MAX)) < min_quiet_zone * module_size - 1.0 + { + return 0.0; + } + + if module_size_ref == 0.0 { + module_size_ref = module_size; + } + + let threshold = module_size_ref * (0.5 + (E2E as u8) as f32 * 0.25) + 0.5; + + // the offset of 0.5 is to make the code less sensitive to quantization errors for small (near 1) module sizes. + // TODO: review once we have upsampling in the binarizer in place. + + for x in 0..LEN { + if (Into::::into(view[x]) - Into::::into(pattern[x]) * module_size_ref).abs() + > threshold + { + return 0.0; + } + } + + module_size +} + +pub fn IsRightGuard( + view: &PatternView, + pattern: &FixedPattern, + minQuietZone: f32, + moduleSizeRef: f32, +) -> bool { + let spaceInPixel = if view.isAtLastBar() { + None + } else { + Some(view.end().unwrap().into()) + }; + + const E2E: bool = false; + + IsPattern::( + view, + pattern, + spaceInPixel, + minQuietZone, + moduleSizeRef, + // None, + ) != 0.0 +} + +pub fn FindLeftGuardBy<'a, const LEN: usize, Pred: Fn(&PatternView, Option) -> bool>( + view: PatternView<'a>, + minSize: usize, + isGuard: Pred, +) -> Result> { + const PREV_IDX: isize = -1; + + if view.size() < minSize { + return Err(Exceptions::ILLEGAL_STATE); + } + + let mut window = view.subView(0, Some(LEN)); + if window.isAtFirstBar() && isGuard(&window, Some(f32::MAX)) { + return Ok(window); + } + let end = Into::::into(view.end().ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?) - minSize; + while (window.start + window.current) < end { + let prev = if let Some(v) = window.try_get_index(PREV_IDX) { + Some(v as f32) + } else { + None + }; + if isGuard(&window, prev) { + return Ok(window); + } + + window.skipPair(); + } + + Err(Exceptions::ILLEGAL_STATE) +} + +pub fn FindLeftGuard<'a, const LEN: usize, const SUM: usize, const IS_SPARCE: bool>( + view: PatternView<'a>, + minSize: usize, + pattern: &FixedPattern, + minQuietZone: f32, +) -> Result> { + FindLeftGuardBy::(view, std::cmp::max(minSize, LEN), |window, spaceInPixel| { + // perform a fast plausability test for 1:1:3:1:1 pattern + // dbg!(window[2], 2 as PatternType * std::cmp::max(window[0], window[4])); + // dbg!(window[2] < std::cmp::max(window[1], window[3])); + if window[2] < 2 as PatternType * std::cmp::max(window[0], window[4]) + || window[2] < std::cmp::max(window[1], window[3]) + { + return false; + } + return IsPattern::( + window, + pattern, + spaceInPixel, + minQuietZone, + 0.0, + ) != 0.0; + }) +} + +pub fn NormalizedE2EPattern<'a, const LEN: usize, const LEN_MINUS_2: usize, const SUM: usize>( + view: &'a PatternView, +) -> [PatternType; LEN_MINUS_2] { + let moduleSize: f32 = Into::::into(view.sum(Some(LEN))) / SUM as f32; + + let mut e2e = [PatternType::default(); LEN_MINUS_2]; + + for i in 0..LEN_MINUS_2 { + let v: f32 = (Into::::into(view[i]) + Into::::into(view[i + 1])) / moduleSize; + e2e[i] = (v + 0.5) as PatternType; + } + + e2e +} + +pub fn NormalizedPattern<'a, const LEN: usize, const SUM: usize>( + view: &'a PatternView, +) -> Result<[PatternType; LEN]> { + let moduleSize: f32 = (Into::::into(view.sum(Some(LEN))) / SUM) as f32; + let mut err = SUM as isize; + let mut is = [PatternType::default(); LEN]; + let mut rs = [0.0; LEN]; + for i in 0..LEN { + // for (int i = 0; i < LEN; i++) { + let v: f32 = Into::::into(view[i]) / moduleSize; + is[i] = (v + 0.5) as PatternType; + rs[i] = v - Into::::into(is[i]); + err -= Into::::into(is[i]) as isize; + } + + if err.abs() > 1 { + return Err(Exceptions::NOT_FOUND); + } + + if err != 0 { + // let mi =if err > 0 { std::max_element(std::begin(rs), std::end(rs)) - std::begin(rs)} + // else {std::min_element(std::begin(rs), std::end(rs)) - std::begin(rs)}; + let mi = if err > 0 { + rs.iter() + .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) + } else { + rs.iter() + .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) + }; + let mi = mi.ok_or(Exceptions::ILLEGAL_STATE)?; + is[*mi as usize] += err as PatternType; + rs[*mi as usize] -= err as f32; + } + + Ok(is) +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum Color { + White = 0, + Black = 1, +} + +impl> From for Color { + fn from(value: T) -> Self { + match value.into() { + 0 => Color::White, + _ => Color::Black, + } + } +} + +pub fn GetPatternRowTP(matrix: &BitMatrix, r: u32, pr: &mut PatternRow, transpose: bool) { + let row = if transpose { + matrix.getCol(r) + } else { + matrix.getRow(r) + }; + + let pixel_states: Vec = row.into(); + + GetPatternRow(&pixel_states, pr) +} + +pub fn GetPatternRow + Copy + Default + From>( + b_row: &[T], + p_row: &mut PatternRow, +) { + p_row.0.clear(); + + if Color::from(p_row.0.first().copied().unwrap_or_default()) == Color::Black { + // first + p_row.0.push(0); + } + + let mut current_color = Color::from(p_row.0.first().copied().unwrap_or_default()); //if p_row.0.first().copied().unwrap_or_default() == 1 {Color::Black} else { Color::White}; + let mut count = 0; + + for bit in b_row.iter() { + let this_color = Color::from(*bit); + + if current_color != this_color { + p_row.0.push(count); + count = 0; + + current_color = this_color; + } + + count += 1; + } + + // dbg!(&p_row.0); + + if count != 0 { + p_row.0.push(count); + } + + if current_color == Color::Black { + p_row.0.push(0); + } +} + +#[cfg(test)] +mod tests { + use crate::common::cpp_essentials::PatternType; + + use super::{GetPatternRow, PatternRow, PatternView}; + const N: usize = 33; + + #[test] + fn all_white() { + for s in 1..=N { + // for (int s = 1; s <= N; ++s) { + let t_in: Vec = vec![0; s]; + // std::vector in(s, 0); + let mut pr = PatternRow::default(); + GetPatternRow(&t_in, &mut pr); + + assert_eq!(pr.0.len(), 1); + assert_eq!(pr.0[0], s as PatternType); + } + } + + #[test] + fn all_black() { + for s in 1..=N { + // for (int s = 1; s <= N; ++s) { + let t_in: Vec = vec![0xff; s]; + let mut pr = PatternRow::default(); + GetPatternRow(&t_in, &mut pr); + + assert_eq!(pr.0.len(), 3); + assert_eq!(pr.0[0], 0); + assert_eq!(pr.0[1], s as PatternType); + assert_eq!(pr.0[2], 0); + } + } + + #[test] + fn black_white() { + for s in 1..=N { + // for (int s = 1; s <= N; ++s) { + let mut t_in: Vec = vec![0; N]; + t_in[..s].copy_from_slice(&vec![1; s]); + // std::fill_n(in.data(), s, 0xff); + let mut pr = PatternRow::default(); + GetPatternRow(&t_in, &mut pr); + + assert_eq!(pr.0.len(), 3); + assert_eq!(pr.0[0], 0); + assert_eq!(pr.0[1], s as PatternType); + assert_eq!(pr.0[2], (N - s) as PatternType); + } + } + + #[test] + fn white_black() { + for s in 0..N { + // for (int s = 0; s < N; ++s) { + let mut t_in: Vec = vec![0xff; N]; + t_in[..s].copy_from_slice(&vec![0; s]); + let mut pr = PatternRow::default(); + GetPatternRow(&t_in, &mut pr); + + assert_eq!(pr.0.len(), 3); + assert_eq!(pr.0[0], s as PatternType); + assert_eq!(pr.0[1], (N - s) as PatternType); + assert_eq!(pr.0[2], 0); + } + } + + #[test] + fn basic_pattern_view() { + let mut p_row = PatternRow::default(); + GetPatternRow( + &[ + 0_u16, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, + ], + &mut p_row, + ); + + let mut pv = PatternView::new(&p_row); + + assert_eq!(pv.data().0, p_row.0); + + assert_eq!(pv[0], 1_u16); + assert_eq!(pv[1], 1_u16); + assert_eq!(pv[4], 2_u16); + assert_eq!(pv[7], 6_u16); + + assert_eq!(pv.index(), 0); + assert!(pv.shift(1)); + assert_eq!(pv.index(), 1); + assert!(pv.skipPair()); + assert_eq!(pv.index(), 3); + } +} diff --git a/src/common/cpp_essentials/regression_line.rs b/src/common/cpp_essentials/regression_line.rs new file mode 100644 index 0000000..6e28d10 --- /dev/null +++ b/src/common/cpp_essentials/regression_line.rs @@ -0,0 +1,236 @@ +use crate::common::Result; +use crate::{Exceptions, Point}; + +use super::RegressionLineTrait; + +#[derive(Clone)] +pub struct RegressionLine { + points: Vec, + direction_inward: Point, + pub(super) a: f32, + pub(super) b: f32, + pub(super) c: f32, + // std::vector _points; + // PointF _directionInward; + // PointF::value_t a = NAN, b = NAN, c = NAN; +} + +impl Default for RegressionLine { + fn default() -> Self { + Self { + points: Default::default(), + direction_inward: Default::default(), + a: f32::NAN, + b: f32::NAN, + c: f32::NAN, + } + } +} + +impl RegressionLineTrait for RegressionLine { + fn points(&self) -> &[Point] { + &self.points + } + + fn length(&self) -> u32 { + if self.points.len() >= 2 { + Point::distance(*self.points.first().unwrap(), *self.points.last().unwrap()) as u32 + } else { + 0 + } + } + + fn isValid(&self) -> bool { + !self.a.is_nan() + } + + fn normal(&self) -> Point { + if self.isValid() { + Point { + x: self.a, + y: self.b, + } + } else { + self.direction_inward + } + } + + fn signedDistance(&self, p: Point) -> f32 { + Point::dot(self.normal(), p) - self.c + } + + fn distance_single(&self, p: Point) -> f32 { + (self.signedDistance(p)).abs() + } + + fn reset(&mut self) { + self.points.clear(); + self.direction_inward = Point { x: 0.0, y: 0.0 }; + self.a = f32::NAN; + self.b = f32::NAN; + self.c = f32::NAN; + } + + fn add(&mut self, p: Point) -> Result<()> { + if self.direction_inward == Point::default() { + return Err(Exceptions::ILLEGAL_STATE); + } + self.points.push(p); + if self.points.len() == 1 { + self.c = Point::dot(self.normal(), p); + } + Ok(()) + } + + fn pop_back(&mut self) { + self.points.pop(); + } + + fn setDirectionInward(&mut self, d: Point) { + self.direction_inward = Point::normalized(d); + } + + fn evaluate_max_distance( + &mut self, + maxSignedDist: Option, + updatePoints: Option, + ) -> bool { + let maxSignedDist = if let Some(m) = maxSignedDist { m } else { -1.0 }; + let updatePoints = if let Some(u) = updatePoints { u } else { false }; + + let mut ret = self.evaluateSelf(); + if maxSignedDist > 0.0 { + let mut points = self.points.clone(); + loop { + let old_points_size = points.len(); + // remove points that are further 'inside' than maxSignedDist or further 'outside' than 2 x maxSignedDist + // auto end = std::remove_if(points.begin(), points.end(), [this, maxSignedDist](auto p) { + // auto sd = this->signedDistance(p); + // return sd > maxSignedDist || sd < -2 * maxSignedDist; + // }); + // points.erase(end, points.end()); + points.retain(|&p| { + let sd = self.signedDistance(p) as f64; + !(sd > maxSignedDist || sd < -2.0 * maxSignedDist) + }); + if old_points_size == points.len() { + break; + } + // #ifdef PRINT_DEBUG + // printf("removed %zu points\n", old_points_size - points.size()); + // #endif + ret = self.evaluate(&points); + } + + if updatePoints { + self.points = points; + } + } + ret + } + + fn isHighRes(&self) -> bool { + let Some(mut min) = self.points.first().copied() else { return false }; + let Some(mut max) = self.points.first().copied() else { return false }; + for p in &self.points { + min.x = f32::min(min.x, p.x); + min.y = f32::min(min.y, p.y); + max.x = f32::max(max.x, p.x); + max.y = f32::max(max.y, p.y); + } + let diff = max - min; + let len = diff.maxAbsComponent(); + let steps = f32::min(diff.x.abs(), diff.y.abs()); + // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal + steps > 2.0 || len > 50.0 + } + + fn evaluate(&mut self, points: &[Point]) -> bool { + let mean = points.iter().sum::() / points.len() as f32; + + let mut sumXX = 0.0; + let mut sumYY = 0.0; + let mut sumXY = 0.0; + for p in points { + // for (auto p = begin; p != end; ++p) { + let d = *p - mean; + sumXX += d.x * d.x; + sumYY += d.y * d.y; + sumXY += d.x * d.y; + } + if sumYY >= sumXX { + let l = (sumYY * sumYY + sumXY * sumXY).sqrt(); + self.a = sumYY / l; + self.b = -sumXY / l; + } else { + let l = (sumXX * sumXX + sumXY * sumXY).sqrt(); + self.a = sumXY / l; + self.b = -sumXX / l; + } + if Point::dot(self.direction_inward, self.normal()) < 0.0 { + // if (dot(_directionInward, normal()) < 0) { + self.a = -self.a; + self.b = -self.b; + } + self.c = Point::dot(self.normal(), mean); // (a*mean.x + b*mean.y); + Point::dot(self.direction_inward, self.normal()) > 0.5 + // angle between original and new direction is at most 60 degree + } + + fn evaluateSelf(&mut self) -> bool { + let mean = self.points.iter().sum::() / self.points.len() as f32; + + let mut sumXX = 0.0; + let mut sumYY = 0.0; + let mut sumXY = 0.0; + for p in &self.points { + // for (auto p = begin; p != end; ++p) { + let d = *p - mean; + sumXX += d.x * d.x; + sumYY += d.y * d.y; + sumXY += d.x * d.y; + } + if sumYY >= sumXX { + let l = (sumYY * sumYY + sumXY * sumXY).sqrt(); + self.a = sumYY / l; + self.b = -sumXY / l; + } else { + let l = (sumXX * sumXX + sumXY * sumXY).sqrt(); + self.a = sumXY / l; + self.b = -sumXX / l; + } + if Point::dot(self.direction_inward, self.normal()) < 0.0 { + // if (dot(_directionInward, normal()) < 0) { + self.a = -self.a; + self.b = -self.b; + } + self.c = Point::dot(self.normal(), mean); // (a*mean.x + b*mean.y); + Point::dot(self.direction_inward, self.normal()) > 0.5 + // angle between original and new direction is at most 60 degree + } + + fn a(&self) -> f32 { + self.a + } + + fn b(&self) -> f32 { + self.b + } + + fn c(&self) -> f32 { + self.c + } +} + +impl RegressionLine { + pub fn with_two_points(point1: Point, point2: Point) -> Self { + let mut new_rl = RegressionLine::default(); + new_rl.evaluate(&[point1, point2]); + new_rl + } + pub fn with_point_slice(points: &[Point]) -> Self { + let mut new_rl = RegressionLine::default(); + new_rl.evaluate(points); + new_rl + } +} diff --git a/src/datamatrix/detector/zxing_cpp_detector/regression_line.rs b/src/common/cpp_essentials/regression_line_trait.rs similarity index 90% rename from src/datamatrix/detector/zxing_cpp_detector/regression_line.rs rename to src/common/cpp_essentials/regression_line_trait.rs index a9ccd90..4fa8bf3 100644 --- a/src/datamatrix/detector/zxing_cpp_detector/regression_line.rs +++ b/src/common/cpp_essentials/regression_line_trait.rs @@ -1,7 +1,7 @@ use crate::common::Result; -use crate::Point; +use crate::{point, Point}; -pub trait RegressionLine { +pub trait RegressionLineTrait { // points: Vec, // direction_inward: Point, @@ -11,8 +11,20 @@ pub trait RegressionLine { // PointF _directionInward; // PointF::value_t a = NAN, b = NAN, c = NAN; - // fn intersect(&self, l1: &T, l2: &T2) - // -> Point; + fn intersect( + l1: &T, + l2: &T2, + ) -> Option { + if !(l1.isValid() && l2.isValid()) { + return None; + } + + let d = l1.a() * l2.b() - l1.b() * l2.a(); + let x = (l1.c() * l2.b() - l1.b() * l2.c()) / d; + let y = (l1.a() * l2.c() - l1.c() * l2.a()) / d; + + Some(point(x, y)) + } // fn evaluate_begin_end(&self, begin: Point, end: Point) -> bool;// { // { @@ -131,4 +143,7 @@ pub trait RegressionLine { // // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal // return steps > 2 || len > 50; // } + fn a(&self) -> f32; + fn b(&self) -> f32; + fn c(&self) -> f32; } diff --git a/src/datamatrix/detector/zxing_cpp_detector/step_result.rs b/src/common/cpp_essentials/step_result.rs similarity index 100% rename from src/datamatrix/detector/zxing_cpp_detector/step_result.rs rename to src/common/cpp_essentials/step_result.rs diff --git a/src/common/cpp_essentials/structured_append.rs b/src/common/cpp_essentials/structured_append.rs new file mode 100644 index 0000000..d992e03 --- /dev/null +++ b/src/common/cpp_essentials/structured_append.rs @@ -0,0 +1,16 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StructuredAppendInfo { + pub index: i32, // -1; + pub count: i32, // = -1; + pub id: String, +} + +impl Default for StructuredAppendInfo { + fn default() -> Self { + Self { + index: -1, + count: -1, + id: Default::default(), + } + } +} diff --git a/src/common/cpp_essentials/util.rs b/src/common/cpp_essentials/util.rs new file mode 100644 index 0000000..97ddde1 --- /dev/null +++ b/src/common/cpp_essentials/util.rs @@ -0,0 +1,68 @@ +use crate::common::Result; +use crate::{Exceptions, Point}; + +use super::{Direction, RegressionLineTrait}; + +#[inline(always)] +pub fn intersect( + l1: &T, + l2: &T2, +) -> Result { + if !(l1.isValid() && l2.isValid()) { + return Err(Exceptions::ILLEGAL_STATE); + } + let d = l1.a() * l2.b() - l1.b() * l2.a(); + let x = (l1.c() * l2.b() - l1.b() * l2.c()) / d; + let y = (l1.a() * l2.c() - l1.c() * l2.a()) / d; + Ok(Point { x, y }) +} + +#[allow(dead_code)] +#[inline(always)] +pub fn opposite(dir: Direction) -> Direction { + if dir == Direction::Left { + Direction::Right + } else { + Direction::Left + } +} + +#[inline(always)] +pub fn UpdateMinMax(min: &mut T, max: &mut T, val: T) { + *min = std::cmp::min(*min, val); + *max = std::cmp::max(*max, val); +} + +#[inline(always)] +pub fn UpdateMinMaxFloat(min: &mut f64, max: &mut f64, val: f64) { + *min = f64::min(*min, val); + *max = f64::max(*max, val); +} + +// template>> +pub fn ToString>(val: T, len: usize) -> Result { + let mut len = len as isize; + let val = val.into(); + let mut val = val as isize; + + let mut result = vec!['0'; len as usize]; + len -= 1; + // std::string result(len--, '0'); + if val < 0 { + return Err(Exceptions::format_with("Invalid value")); + } + while len >= 0 && val != 0 { + result[len as usize] = char::from(b'0' + (val % 10) as u8); + // result.replace_range((len as usize)..(len as usize), &char::from(b'0' + (val % 10) as u8).to_string()); + + len -= 1; + val /= 10; + } + // for (; len >= 0 && val != 0; --len, val /= 10) { + // result[len] = '0' + val % 10;} + if val != 0 { + return Err(Exceptions::format_with("Invalid value")); + } + + Ok(result.iter().collect()) +} diff --git a/src/datamatrix/detector/zxing_cpp_detector/value.rs b/src/common/cpp_essentials/value.rs similarity index 100% rename from src/datamatrix/detector/zxing_cpp_detector/value.rs rename to src/common/cpp_essentials/value.rs diff --git a/src/common/default_grid_sampler.rs b/src/common/default_grid_sampler.rs index 94964d7..8e80e2f 100644 --- a/src/common/default_grid_sampler.rs +++ b/src/common/default_grid_sampler.rs @@ -19,9 +19,9 @@ // import com.google.zxing.NotFoundException; use crate::common::Result; -use crate::{Exceptions, Point}; +use crate::{point, Exceptions, Point}; -use super::{BitMatrix, GridSampler, PerspectiveTransform, Quadrilateral, SamplerControl}; +use super::{BitMatrix, GridSampler, SamplerControl}; /** * @author Sean Owen @@ -30,87 +30,70 @@ use super::{BitMatrix, GridSampler, PerspectiveTransform, Quadrilateral, Sampler pub struct DefaultGridSampler; impl GridSampler for DefaultGridSampler { - fn sample_grid_detailed( - &self, - image: &BitMatrix, - dimensionX: u32, - dimensionY: u32, - dst: Quadrilateral, - src: Quadrilateral, - ) -> Result { - let transform = PerspectiveTransform::quadrilateralToQuadrilateral(dst, src)?; - - self.sample_grid( - image, - dimensionX, - dimensionY, - &[SamplerControl::new(dimensionX, dimensionY, transform)], - ) - } - fn sample_grid( &self, image: &BitMatrix, dimensionX: u32, dimensionY: u32, controls: &[SamplerControl], - ) -> Result { - if dimensionX == 0 || dimensionY == 0 { + ) -> Result<(BitMatrix, [Point; 4])> { + if dimensionX <= 0 || dimensionY <= 0 { return Err(Exceptions::NOT_FOUND); } - let mut bits = BitMatrix::new(dimensionX, dimensionY)?; - let mut points = vec![Point::default(); dimensionX as usize]; - for y in 0..dimensionY { - // for (int y = 0; y < dimensionY; y++) { - let max = points.len(); - let i_value = y as f32 + 0.5; - let mut x = 0; - while x < max { - // for (int x = 0; x < max; x += 2) { - points[x].x = (x as f32) + 0.5; - points[x].y = i_value; - x += 1; - } - controls - .first() - .unwrap() - .transform - .transform_points_single(&mut points); - // Quick check to see if points transformed to something inside the image; - // sufficient to check the endpoints - self.checkAndNudgePoints(image, &mut points)?; - // try { - let mut x = 0; - while x < max { - // for (int x = 0; x < max; x += 2) { - // if points[x] as u32 >= image.getWidth() || points[x + 1] as u32 >= image.getHeight() - // { - // return Err(Exceptions::notFound( - // "index out of bounds, see documentation in file for explanation".to_owned(), - // )); - // } - if image - .try_get(points[x].x as u32, points[x].y as u32) - .ok_or(Exceptions::not_found_with( - "index out of bounds, see documentation in file for explanation", - ))? - { - // Black(-ish) pixel - bits.set(x as u32, y); + + for SamplerControl { p0, p1, transform } in controls { + // To deal with remaining examples (see #251 and #267) of "numercial instabilities" that have not been + // prevented with the Quadrilateral.h:IsConvex() check, we check for all boundary points of the grid to + // be inside. + let isInside = + |p: Point| -> bool { image.is_in(transform.transform_point(p.centered())) }; + for y in (p0.y as i32)..(p1.y as i32) { + // for (int y = y0; y < y1; ++y) + if !isInside(point(p0.x, y as f32)) || !isInside(point(p1.x - 1.0, y as f32)) { + return Err(Exceptions::NOT_FOUND); + } + } + for x in (p0.x as i32)..(p1.x as i32) { + // for (int x = x0; x < x1; ++x) + if !isInside(point(x as f32, p0.y)) || !isInside(point(x as f32, p1.y - 1.0)) { + return Err(Exceptions::NOT_FOUND); } - x += 1; } - // } catch (ArrayIndexOutOfBoundsException aioobe) { - // // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting - // // transform gets "twisted" such that it maps a straight line of points to a set of points - // // whose endpoints are in bounds, but others are not. There is probably some mathematical - // // way to detect this about the transformation that I don't know yet. - // // This results in an ugly runtime exception despite our clever checks above -- can't have - // // that. We could check each point's coordinates but that feels duplicative. We settle for - // // catching and wrapping ArrayIndexOutOfBoundsException. - // throw NotFoundException.getNotFoundInstance(); - // } } - Ok(bits) + + let mut bits = BitMatrix::new(dimensionX, dimensionY)?; + for SamplerControl { p0, p1, transform } in controls { + // for (auto&& [x0, x1, y0, y1, mod2Pix] : rois) { + for y in (p0.y as i32)..(p1.y as i32) { + // for (int y = y0; y < y1; ++y) + for x in (p0.x as i32)..(p1.x as i32) { + // for (int x = x0; x < x1; ++x) { + let p = transform.transform_point(Point::from((x, y)).centered()); //mod2Pix(centered(PointI{x, y})); + + if image.get_point(p) { + bits.set(x as u32, y as u32); + } + } + } + } + + // dbg!(image.to_string()); + // dbg!(bits.to_string()); + + let projectCorner = |p: Point| -> Point { + for SamplerControl { p0, p1, transform } in controls { + if p0.x <= p.x && p.x <= p1.x && p0.y <= p.y && p.y <= p1.y { + return transform.transform_point(p) + point(0.5, 0.5); + } + } + Point::default() + }; + + let tl = projectCorner(Point::default()); + let tr = projectCorner(Point::from((dimensionX, 0))); + let bl = projectCorner(Point::from((dimensionX, dimensionY))); + let br = projectCorner(Point::from((0, dimensionX))); + + Ok((bits, [tl, tr, bl, br])) } } diff --git a/src/common/eci.rs b/src/common/eci.rs index da47109..699a529 100644 --- a/src/common/eci.rs +++ b/src/common/eci.rs @@ -1,8 +1,12 @@ use std::fmt::Display; +use crate::Exceptions; + use super::CharacterSet; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +use crate::common::Result; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum Eci { Unknown = -1, Cp437 = 2, // obsolete @@ -44,6 +48,19 @@ impl Eci { pub fn can_encode(self) -> bool { (self as i32) >= 899 } + + pub fn try_from_i32(value: i32) -> Result { + let v = Self::from(value); + if v == Eci::Unknown { + Err(Exceptions::ILLEGAL_ARGUMENT) + } else { + Ok(v) + } + } + + pub fn try_from_u32(value: u32) -> Result { + Self::try_from_i32(value as i32) + } } impl From for Eci { diff --git a/src/common/eci_string_builder.rs b/src/common/eci_string_builder.rs index a43dd6a..eb1792c 100644 --- a/src/common/eci_string_builder.rs +++ b/src/common/eci_string_builder.rs @@ -21,21 +21,28 @@ // import java.nio.charset.Charset; // import java.nio.charset.StandardCharsets; -use std::fmt; +use std::{ + collections::{HashMap, HashSet}, + fmt::{self, Display}, +}; -use super::{CharacterSet, Eci}; +use crate::{pdf417::decoder::ec, BarcodeFormat}; + +use super::{CharacterSet, Eci, StringUtils}; /** * Class that converts a sequence of ECIs and bytes into a string * * @author Alex Geller */ -#[derive(Default)] +#[derive(Default, PartialEq, Eq, Debug, Clone)] pub struct ECIStringBuilder { - is_eci: bool, + pub has_eci: bool, eci_result: Option, bytes: Vec, eci_positions: Vec<(Eci, usize, usize)>, // (Eci, start, end) + pub symbology: SymbologyIdentifier, + eci_list: HashSet, } impl ECIStringBuilder { @@ -44,7 +51,9 @@ impl ECIStringBuilder { eci_result: None, bytes: Vec::with_capacity(initial_capacity), eci_positions: Vec::default(), - is_eci: false, + has_eci: false, + symbology: SymbologyIdentifier::default(), + eci_list: HashSet::default(), } } @@ -107,26 +116,55 @@ impl ECIStringBuilder { */ pub fn append_eci(&mut self, eci: Eci) { self.eci_result = None; - if !self.is_eci && eci != Eci::ISO8859_1 { - self.is_eci = true; + + if !self.has_eci && eci != Eci::ISO8859_1 { + self.has_eci = true; } - if self.is_eci { + if self.has_eci { if let Some(last) = self.eci_positions.last_mut() { last.2 = self.bytes.len() } self.eci_positions.push((eci, self.bytes.len(), 0)); + + self.eci_list.insert(eci); + + if self.eci_list.len() == 1 && (self.eci_list.contains(&Eci::Unknown)) { + self.has_eci = false; + self.eci_positions.clear(); + } } } + /// Change the current encoding characterset, finding an eci to do so + pub fn switch_encoding(&mut self, charset: CharacterSet, is_eci: bool) { + //self.append_eci(Eci::from(charset)) + if is_eci && !self.has_eci { + self.eci_positions.clear(); + } + if is_eci || !self.has_eci + //{self.eci_positions.push_back({eci, Size(bytes)});} + { + // self.append_eci(Eci::from(charset)) + if let Some(last) = self.eci_positions.last_mut() { + last.2 = self.bytes.len() + } + + self.eci_positions + .push((Eci::from(charset), self.bytes.len(), 0)); + } + + self.has_eci |= is_eci; + } + /// Finishes encoding anything in the buffer using the current ECI and resets. /// /// This function can panic pub fn encodeCurrentBytesIfAny(&self) -> String { let mut encoded_string = String::with_capacity(self.bytes.len()); // First encode the first set - let (_, end, _) = + let (_eci, end, _) = *self .eci_positions .first() @@ -136,6 +174,10 @@ impl ECIStringBuilder { &Self::encode_segment(&self.bytes[0..end], Eci::ISO8859_1).unwrap_or_default(), ); + if end == self.bytes.len() { + return encoded_string; + } + // If there are more sets, encode each of them in turn for (eci, eci_start, eci_end) in &self.eci_positions { // let (_,end) = *self.eci_positions.first().unwrap_or(&(*eci, self.bytes.len())); @@ -154,20 +196,41 @@ impl ECIStringBuilder { } fn encode_segment(bytes: &[u8], eci: Eci) -> Option { + let mut not_encoded_yet = true; let mut encoded_string = String::with_capacity(bytes.len()); if ![Eci::Binary, Eci::Unknown].contains(&eci) { if eci == Eci::UTF8 { if !bytes.is_empty() { encoded_string.push_str(&CharacterSet::UTF8.decode(bytes).ok()?); + not_encoded_yet = false; } else { return None; } } else if !bytes.is_empty() { encoded_string.push_str(&CharacterSet::from(eci).decode(bytes).ok()?); + not_encoded_yet = false; } else { return None; } - } else { + } else if eci == Eci::Unknown { + /* // This probably should never be used, it's here just in case I don't understand what's + // going on. + let cs = CharacterSet::from(Eci::ISO8859_1); + if let Ok(enc_str) = cs.decode(bytes) { + encoded_string.push_str(&enc_str); + not_encoded_yet = false; + } + + else */ + if let Some(found_encoding) = StringUtils::guessCharset(bytes, &HashMap::default()) { + if let Ok(found_encoded_str) = found_encoding.decode(bytes) { + encoded_string.push_str(&found_encoded_str); + not_encoded_yet = false; + } + } + } + + if not_encoded_yet { for byte in bytes { encoded_string.push(char::from(*byte)) } @@ -198,6 +261,11 @@ impl ECIStringBuilder { self.bytes.len() } + /// Reserve an additional number of bytes for storage + pub fn reserve(&mut self, additional: usize) { + self.bytes.reserve(additional); + } + /** * @return true iff nothing has been appended */ @@ -210,6 +278,14 @@ impl ECIStringBuilder { self } + + // pub fn list_ecis(&self) -> HashSet { + // let mut hs = HashSet::new(); + // self.eci_positions.iter().for_each(|pos| { + // hs.insert(pos.0); + // }); + // hs + // } } impl fmt::Display for ECIStringBuilder { @@ -221,3 +297,57 @@ impl fmt::Display for ECIStringBuilder { } } } + +impl std::ops::AddAssign for ECIStringBuilder { + fn add_assign(&mut self, rhs: u8) { + self.append_byte(rhs) + } +} + +impl std::ops::AddAssign for ECIStringBuilder { + fn add_assign(&mut self, rhs: String) { + self.append_string(&rhs) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ContentType { + Text, + Binary, + Mixed, + GS1, + ISO15434, + UnknownECI, +} +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum AIFlag { + None, + GS1, + AIM, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct SymbologyIdentifier { + //char code = 0, modifier = 0, eciModifierOffset = 0; + pub code: u8, + pub modifier: u8, + pub eciModifierOffset: u8, + pub aiFlag: AIFlag, + // AIFlag aiFlag = AIFlag::None; + + // std::string toString(bool hasECI = false) const + // { + // return code ? ']' + std::string(1, code) + static_cast(modifier + eciModifierOffset * hasECI) : std::string(); + // } +} + +impl Default for SymbologyIdentifier { + fn default() -> Self { + Self { + code: 0, + modifier: 0, + eciModifierOffset: 0, + aiFlag: AIFlag::None, + } + } +} diff --git a/src/common/grid_sampler.rs b/src/common/grid_sampler.rs index 7fc5a4e..7678cb7 100644 --- a/src/common/grid_sampler.rs +++ b/src/common/grid_sampler.rs @@ -95,7 +95,16 @@ pub trait GridSampler { dimensionY: u32, dst: Quadrilateral, src: Quadrilateral, - ) -> Result; + ) -> Result<(BitMatrix, [Point; 4])> { + let transform = PerspectiveTransform::quadrilateralToQuadrilateral(dst, src)?; + + self.sample_grid( + image, + dimensionX, + dimensionY, + &[SamplerControl::new(dimensionX, dimensionY, transform)], + ) + } fn sample_grid( &self, @@ -103,7 +112,76 @@ pub trait GridSampler { dimensionX: u32, dimensionY: u32, controls: &[SamplerControl], - ) -> Result; + ) -> Result<(BitMatrix, [Point; 4])> { + if dimensionX == 0 || dimensionY == 0 { + return Err(Exceptions::NOT_FOUND); + } + let mut bits = BitMatrix::new(dimensionX, dimensionY)?; + let mut points = vec![Point::default(); dimensionX as usize]; + for y in 0..dimensionY { + // for (int y = 0; y < dimensionY; y++) { + let max = points.len(); + let i_value = y as f32 + 0.5; + let mut x = 0; + while x < max { + // for (int x = 0; x < max; x += 2) { + points[x].x = (x as f32) + 0.5; + points[x].y = i_value; + x += 1; + } + + controls + .first() + .unwrap() + .transform + .transform_points_single(&mut points); + // Quick check to see if points transformed to something inside the image; + // sufficient to check the endpoints + self.checkAndNudgePoints(image, &mut points)?; + // try { + let mut x = 0; + while x < max { + // for (int x = 0; x < max; x += 2) { + // if points[x] as u32 >= image.getWidth() || points[x + 1] as u32 >= image.getHeight() + // { + // return Err(Exceptions::notFound( + // "index out of bounds, see documentation in file for explanation".to_owned(), + // )); + // } + if image + .try_get(points[x].x as u32, points[x].y as u32) + .ok_or(Exceptions::not_found_with( + "index out of bounds, see documentation in file for explanation", + ))? + { + // Black(-ish) pixel + bits.set(x as u32, y); + } + x += 1; + } + // } catch (ArrayIndexOutOfBoundsException aioobe) { + // // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting + // // transform gets "twisted" such that it maps a straight line of points to a set of points + // // whose endpoints are in bounds, but others are not. There is probably some mathematical + // // way to detect this about the transformation that I don't know yet. + // // This results in an ugly runtime exception despite our clever checks above -- can't have + // // that. We could check each point's coordinates but that feels duplicative. We settle for + // // catching and wrapping ArrayIndexOutOfBoundsException. + // throw NotFoundException.getNotFoundInstance(); + // } + } + // dbg!(bits.to_string()); + + Ok(( + bits, + [ + Point::default(), + Point::default(), + Point::default(), + Point::default(), + ], + )) + } /** *

Checks a set of points that have been transformed to sample points on an image against diff --git a/src/common/mod.rs b/src/common/mod.rs index 8327876..d011f1d 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -112,6 +112,8 @@ pub use eci::*; mod quad; pub use quad::*; +pub mod cpp_essentials; + #[cfg(feature = "otsu_level")] mod otsu_level_binarizer; #[cfg(feature = "otsu_level")] diff --git a/src/common/perspective_transform.rs b/src/common/perspective_transform.rs index 5e42be3..5c291bd 100644 --- a/src/common/perspective_transform.rs +++ b/src/common/perspective_transform.rs @@ -29,6 +29,7 @@ use super::Quadrilateral; * * @author Sean Owen */ +#[derive(Debug, Copy, Clone, PartialEq)] pub struct PerspectiveTransform { a11: f32, a12: f32, @@ -81,15 +82,28 @@ impl PerspectiveTransform { Ok(s_to_q * q_to_s) } + pub fn transform_point(&self, point: Point) -> Point { + let x = point.x; + let y = point.y; + let denominator = self.a13 * x + self.a23 * y + self.a33; + Point::new( + (self.a11 * x + self.a21 * y + self.a31) / denominator, + (self.a12 * x + self.a22 * y + self.a32) / denominator, + ) + } + pub fn transform_points_single(&self, points: &mut [Point]) { for point in points.iter_mut() { - // for (int i = 0; i < maxI; i += 2) { - let x = point.x; - let y = point.y; - let denominator = self.a13 * x + self.a23 * y + self.a33; - point.x = (self.a11 * x + self.a21 * y + self.a31) / denominator; - point.y = (self.a12 * x + self.a22 * y + self.a32) / denominator; + *point = self.transform_point(Point::new(point.x, point.y)); } + // for point in points.iter_mut() { + // // for (int i = 0; i < maxI; i += 2) { + // let x = point.x; + // let y = point.y; + // let denominator = self.a13 * x + self.a23 * y + self.a33; + // point.x = (self.a11 * x + self.a21 * y + self.a31) / denominator; + // point.y = (self.a12 * x + self.a22 * y + self.a32) / denominator; + // } } pub fn transform_points_double(&self, x_values: &mut [f32], y_valuess: &mut [f32]) { diff --git a/src/common/quad.rs b/src/common/quad.rs index 9873d8b..4673eb1 100644 --- a/src/common/quad.rs +++ b/src/common/quad.rs @@ -1,4 +1,6 @@ -use crate::Point; +use crate::{point, Point}; + +use super::PerspectiveTransform; #[derive(Clone, Copy, Debug)] pub struct Quadrilateral(pub [Point; 4]); @@ -51,8 +53,8 @@ impl Quadrilateral { impl Quadrilateral { #[allow(dead_code)] - pub fn rectangle(width: i32, height: i32, margin: Option) -> Quadrilateral { - let margin = margin.unwrap_or(0); + pub fn rectangle(width: i32, height: i32, margin: Option) -> Quadrilateral { + let margin = margin.unwrap_or(0.0); Quadrilateral([ Point { @@ -74,6 +76,16 @@ impl Quadrilateral { ]) } + pub fn rectangle_from_xy(x0: f32, x1: f32, y0: f32, y1: f32, o: Option) -> Self { + let o = o.unwrap_or(0.5); + Quadrilateral::from([ + point(x0 + o, y0 + o), + point(x1 + o, y0 + o), + point(x1 + o, y1 + o), + point(x0 + o, y1 + o), + ]) + } + #[allow(dead_code)] pub fn centered_square(size: i32) -> Quadrilateral { Self::scale( @@ -171,7 +183,7 @@ impl Quadrilateral { let mirror = if let Some(m) = mirror { m } else { false }; - let mut res = self.clone(); + let mut res = *self; res.0.rotate_left(((n + 4) % 4) as usize); // std::rotate_copy(q.begin(), q.begin() + ((n + 4) % 4), q.end(), res.begin()); if mirror { @@ -208,6 +220,33 @@ impl Quadrilateral { !(x || y) } + + pub fn blend(a: &Quadrilateral, b: &Quadrilateral) -> Self { + let c = a[0]; + let dist2First = |a, b| Point::distance(a, c) < Point::distance(b, c); + // rotate points such that the the two topLeft points are closest to each other + let min_element = + b.0.iter() + .copied() + .min_by(|a, b| match dist2First(*a, *b) { + true => std::cmp::Ordering::Less, + false => std::cmp::Ordering::Greater, + }) + .unwrap_or_default(); + let offset = + b.0.iter() + .position(|v| *v == min_element) + .unwrap_or_default(); + // let offset = std::min_element(b.begin(), b.end(), dist2First) - b.begin(); + + let mut res = Quadrilateral::default(); + for i in 0..4 { + // for (int i = 0; i < 4; ++i){ + res[i] = (a[i] + b[(i + offset) % 4]) / 2.0; + } + + res + } } impl Default for Quadrilateral { @@ -215,3 +254,23 @@ impl Default for Quadrilateral { Self([Point { x: 0.0, y: 0.0 }; 4]) } } + +impl std::ops::Index for Quadrilateral { + type Output = Point; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl std::ops::IndexMut for Quadrilateral { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } +} + +impl From<[Point; 4]> for Quadrilateral { + fn from(value: [Point; 4]) -> Self { + Self(value) + } +} diff --git a/src/datamatrix/detector/datamatrix_detector.rs b/src/datamatrix/detector/datamatrix_detector.rs index 88a973c..e417ce9 100644 --- a/src/datamatrix/detector/datamatrix_detector.rs +++ b/src/datamatrix/detector/datamatrix_detector.rs @@ -343,7 +343,8 @@ impl<'a> Detector<'_> { ); let src = Quadrilateral::new(topRight, topLeft, bottomRight, bottomLeft); - sampler.sample_grid_detailed(image, dimensionX, dimensionY, dst, src) + let (res, _) = sampler.sample_grid_detailed(image, dimensionX, dimensionY, dst, src)?; + Ok(res) } /** diff --git a/src/datamatrix/detector/zxing_cpp_detector/cpp_new_detector.rs b/src/datamatrix/detector/zxing_cpp_detector/cpp_new_detector.rs index 761cba3..e09b8dc 100644 --- a/src/datamatrix/detector/zxing_cpp_detector/cpp_new_detector.rs +++ b/src/datamatrix/detector/zxing_cpp_detector/cpp_new_detector.rs @@ -14,9 +14,12 @@ macro_rules! CHECK { use std::{cell::RefCell, rc::Rc}; use crate::{ - common::{BitMatrix, DefaultGridSampler, GridSampler, Quadrilateral, Result}, + common::{ + cpp_essentials::RegressionLineTrait, BitMatrix, DefaultGridSampler, GridSampler, + Quadrilateral, Result, + }, datamatrix::detector::{ - zxing_cpp_detector::{util::intersect, BitMatrixCursor, RegressionLine}, + zxing_cpp_detector::{util::intersect, BitMatrixCursorTrait}, DatamatrixDetectorResult, }, point, @@ -230,8 +233,10 @@ fn Scan( CHECK!(res.is_ok()); + let (res, _) = res?; + return Ok(DatamatrixDetectorResult::new( - res?, + res, sourcePoints.points().to_vec(), )); } diff --git a/src/datamatrix/detector/zxing_cpp_detector/mod.rs b/src/datamatrix/detector/zxing_cpp_detector/mod.rs index 1842d6d..fa222b0 100644 --- a/src/datamatrix/detector/zxing_cpp_detector/mod.rs +++ b/src/datamatrix/detector/zxing_cpp_detector/mod.rs @@ -1,18 +1,11 @@ -mod bitmatrix_cursor; mod cpp_new_detector; -mod direction; -mod dm_regression_line; -mod edge_tracer; -mod regression_line; -mod step_result; -pub(self) mod util; -mod value; -pub(self) use bitmatrix_cursor::*; +pub(self) use crate::common::cpp_essentials::bitmatrix_cursor_trait::*; +pub(self) use crate::common::cpp_essentials::direction::*; +pub(self) use crate::common::cpp_essentials::dm_regression_line::*; +pub(self) use crate::common::cpp_essentials::edge_tracer::*; +pub(self) use crate::common::cpp_essentials::regression_line::*; +pub(self) use crate::common::cpp_essentials::step_result::*; +pub(self) use crate::common::cpp_essentials::util; +pub(self) use crate::common::cpp_essentials::value::*; pub use cpp_new_detector::detect; -pub(self) use direction::*; -pub(self) use dm_regression_line::*; -pub(self) use edge_tracer::*; -pub(self) use regression_line::*; -pub(self) use step_result::*; -pub(self) use value::*; diff --git a/src/datamatrix/detector/zxing_cpp_detector/util.rs b/src/datamatrix/detector/zxing_cpp_detector/util.rs deleted file mode 100644 index c2c4180..0000000 --- a/src/datamatrix/detector/zxing_cpp_detector/util.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::common::Result; -use crate::{Exceptions, Point}; - -use super::{DMRegressionLine, Direction, RegressionLine}; - -#[inline(always)] -pub fn float_min(a: T, b: T) -> T { - if a > b { - b - } else { - a - } -} - -#[inline(always)] -pub fn float_max(a: T, b: T) -> T { - if a < b { - b - } else { - a - } -} - -#[inline(always)] -pub fn intersect(l1: &DMRegressionLine, l2: &DMRegressionLine) -> Result { - if !(l1.isValid() && l2.isValid()) { - return Err(Exceptions::ILLEGAL_STATE); - } - let d = l1.a * l2.b - l1.b * l2.a; - let x = (l1.c * l2.b - l1.b * l2.c) / d; - let y = (l1.a * l2.c - l1.c * l2.a) / d; - Ok(Point { x, y }) -} - -#[allow(dead_code)] -#[inline(always)] -pub fn opposite(dir: Direction) -> Direction { - if dir == Direction::Left { - Direction::Right - } else { - Direction::Left - } -} diff --git a/src/exceptions.rs b/src/exceptions.rs index 6001fa5..ae2788d 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Error, Debug, PartialEq, Eq)] +#[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum Exceptions { #[error("IllegalArgumentException{}", if .0.is_empty() { String::new() } else { format!(" - {}", .0) })] IllegalArgumentException(String), diff --git a/src/maxicode/detector.rs b/src/maxicode/detector.rs index 8f6546a..4294311 100644 --- a/src/maxicode/detector.rs +++ b/src/maxicode/detector.rs @@ -356,7 +356,7 @@ pub fn detect(image: &BitMatrix, try_harder: bool) -> Result { - QRCodeReader::default().decode_with_hints(image, &self.hints) + let cpp = QrReader::default().decode_with_hints(image, &self.hints); + if cpp.is_ok() { + cpp + } else { + QRCodeReader::default().decode_with_hints(image, &self.hints) + } + } + BarcodeFormat::MICRO_QR_CODE => { + QrReader::default().decode_with_hints(image, &self.hints) } BarcodeFormat::DATA_MATRIX => { DataMatrixReader::default().decode_with_hints(image, &self.hints) @@ -196,6 +214,9 @@ impl MultiFormatReader { } } + if let Ok(res) = QrReader::default().decode_with_hints(image, &self.hints) { + return Ok(res); + } if let Ok(res) = QRCodeReader::default().decode_with_hints(image, &self.hints) { return Ok(res); } diff --git a/src/multi_use_multi_format_reader.rs b/src/multi_use_multi_format_reader.rs index 16ecb18..f78e69e 100644 --- a/src/multi_use_multi_format_reader.rs +++ b/src/multi_use_multi_format_reader.rs @@ -17,6 +17,7 @@ use std::collections::{HashMap, HashSet}; use crate::common::Result; +use crate::qrcode::cpp_port::QrReader; use crate::{ aztec::AztecReader, datamatrix::DataMatrixReader, maxicode::MaxiCodeReader, oned::MultiFormatOneDReader, pdf417::PDF417Reader, qrcode::QRCodeReader, BarcodeFormat, @@ -43,6 +44,7 @@ pub struct MultiUseMultiFormatReader { aztec_reader: AztecReader, pdf417_reader: PDF417Reader, maxicode_reader: MaxiCodeReader, + cpp_qrcode_reader: QrReader, } impl Reader for MultiUseMultiFormatReader { @@ -84,6 +86,7 @@ impl Reader for MultiUseMultiFormatReader { self.aztec_reader.reset(); self.pdf417_reader.reset(); self.maxicode_reader.reset(); + self.cpp_qrcode_reader.reset(); } } @@ -147,8 +150,16 @@ impl MultiUseMultiFormatReader { image.get_black_matrix_mut().flip_self(); let res = self.decode_formats(image); if res.is_ok() { - return res; + let mut r = res.unwrap(); + r.putMetadata( + crate::RXingResultMetadataType::IS_INVERTED, + crate::RXingResultMetadataValue::IsInverted(true), + ); + return Ok(r); } + // if res.is_ok() { + // return res; + // } } Err(Exceptions::NOT_FOUND) } @@ -174,7 +185,15 @@ impl MultiUseMultiFormatReader { for possible_format in self.possible_formats.iter() { let res = match possible_format { BarcodeFormat::QR_CODE => { - self.qr_code_reader.decode_with_hints(image, &self.hints) + let a = self.cpp_qrcode_reader.decode_with_hints(image, &self.hints); + if a.is_ok() { + a + } else { + self.qr_code_reader.decode_with_hints(image, &self.hints) + } + } + BarcodeFormat::MICRO_QR_CODE => { + self.cpp_qrcode_reader.decode_with_hints(image, &self.hints) } BarcodeFormat::DATA_MATRIX => self .data_matrix_reader @@ -203,7 +222,9 @@ impl MultiUseMultiFormatReader { return Ok(res); } } - + if let Ok(res) = self.cpp_qrcode_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } if let Ok(res) = self.qr_code_reader.decode_with_hints(image, &self.hints) { return Ok(res); } diff --git a/src/oned/one_d_code_writer.rs b/src/oned/one_d_code_writer.rs index a73b59e..b57c589 100644 --- a/src/oned/one_d_code_writer.rs +++ b/src/oned/one_d_code_writer.rs @@ -195,6 +195,6 @@ impl Writer for L { impl OneDimensionalCodeWriter for L { fn encode_oned(&self, _contents: &str) -> Result> { - todo!() + unimplemented!() } } diff --git a/src/oned/upc_ean_reader.rs b/src/oned/upc_ean_reader.rs index caa984a..11b1273 100644 --- a/src/oned/upc_ean_reader.rs +++ b/src/oned/upc_ean_reader.rs @@ -490,7 +490,7 @@ pub trait UPCEANReader: OneDReader { pub(crate) struct StandInStruct; impl UPCEANReader for StandInStruct { fn getBarcodeFormat(&self) -> BarcodeFormat { - todo!() + unimplemented!() } fn decodeMiddle( @@ -499,7 +499,7 @@ impl UPCEANReader for StandInStruct { _startRange: &[usize; 2], _resultString: &mut String, ) -> Result { - todo!() + unimplemented!() } } impl OneDReader for StandInStruct { @@ -509,13 +509,13 @@ impl OneDReader for StandInStruct { _row: &BitArray, _hints: &crate::DecodingHintDictionary, ) -> Result { - todo!() + unimplemented!() } } impl Reader for StandInStruct { fn decode(&mut self, _image: &mut crate::BinaryBitmap) -> Result { - todo!() + unimplemented!() } fn decode_with_hints( @@ -523,7 +523,7 @@ impl Reader for StandInStruct { _image: &mut crate::BinaryBitmap, _hints: &crate::DecodingHintDictionary, ) -> Result { - todo!() + unimplemented!() } } diff --git a/src/qrcode/cpp_port/bitmatrix_parser.rs b/src/qrcode/cpp_port/bitmatrix_parser.rs new file mode 100644 index 0000000..4a10a40 --- /dev/null +++ b/src/qrcode/cpp_port/bitmatrix_parser.rs @@ -0,0 +1,256 @@ +// /* +// * Copyright 2016 Nu-book Inc. +// * Copyright 2016 ZXing authors +// */ +// // SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{BitMatrix, Result}, + qrcode::decoder::{ErrorCorrectionLevel, FormatInformation, Version, VersionRef}, + Exceptions, +}; + +use super::{data_mask::GetDataMaskBit, detector::AppendBit}; + +pub fn getBit(bitMatrix: &BitMatrix, x: u32, y: u32, mirrored: Option) -> bool { + let mirrored = mirrored.unwrap_or(false); + if mirrored { + bitMatrix.get(y, x) + } else { + bitMatrix.get(x, y) + } +} + +pub fn hasValidDimension(bitMatrix: &BitMatrix, isMicro: bool) -> bool { + let dimension = bitMatrix.height(); + if (isMicro) { + dimension >= 11 && dimension <= 17 && (dimension % 2) == 1 + } else { + dimension >= 21 && dimension <= 177 && (dimension % 4) == 1 + } +} + +pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result { + let dimension = bitMatrix.height(); + + let mut version = Version::FromDimension(dimension)?; + + if (version.getVersionNumber() < 7) { + return Ok(version); + } + + for mirror in [false, true] { + // for (bool mirror : {false, true}) { + // Read top-right/bottom-left version info: 3 wide by 6 tall (depending on mirrored) + let mut versionBits = 0; + for y in (0..=5).rev() { + // for (int y = 5; y >= 0; --y) { + for x in ((dimension - 11)..=(dimension - 9)).rev() { + // for (int x = dimension - 9; x >= dimension - 11; --x) { + AppendBit(&mut versionBits, getBit(bitMatrix, x, y, Some(mirror))); + } + } + + version = Version::DecodeVersionInformation(versionBits, 0)?; // THIS MIGHT BE WRONG todo!() + if (version.getDimensionForVersion() == dimension) { + return Ok(version); + } + } + + return Err(Exceptions::FORMAT); +} + +pub fn ReadFormatInformation(bitMatrix: &BitMatrix, isMicro: bool) -> Result { + if (!hasValidDimension(bitMatrix, isMicro)) { + return Err(Exceptions::FORMAT); + } + + if (isMicro) { + // Read top-left format info bits + let mut formatInfoBits = 0; + for x in 1..9 { + // for (int x = 1; x < 9; x++) + AppendBit(&mut formatInfoBits, getBit(bitMatrix, x, 8, None)); + } + for y in (1..=7).rev() { + // for (int y = 7; y >= 1; y--) + AppendBit(&mut formatInfoBits, getBit(bitMatrix, 8, y, None)); + } + + return Ok(FormatInformation::DecodeMQR(formatInfoBits as u32)); + } + + // Read top-left format info bits + let mut formatInfoBits1 = 0; + for x in 0..6 { + // for (int x = 0; x < 6; x++) + AppendBit(&mut formatInfoBits1, getBit(bitMatrix, x, 8, None)); + } + // .. and skip a bit in the timing pattern ... + AppendBit(&mut formatInfoBits1, getBit(bitMatrix, 7, 8, None)); + AppendBit(&mut formatInfoBits1, getBit(bitMatrix, 8, 8, None)); + AppendBit(&mut formatInfoBits1, getBit(bitMatrix, 8, 7, None)); + // .. and skip a bit in the timing pattern ... + for y in (0..=5).rev() { + // for (int y = 5; y >= 0; y--) + AppendBit(&mut formatInfoBits1, getBit(bitMatrix, 8, y, None)); + } + + // Read the top-right/bottom-left pattern including the 'Dark Module' from the bottom-left + // part that has to be considered separately when looking for mirrored symbols. + // See also FormatInformation::DecodeQR + let dimension = bitMatrix.height(); + let mut formatInfoBits2 = 0; + for y in ((dimension - 8)..=(dimension - 1)).rev() { + // for (int y = dimension - 1; y >= dimension - 8; y--) + AppendBit(&mut formatInfoBits2, getBit(bitMatrix, 8, y, None)); + } + for x in (dimension - 8)..dimension { + // for (int x = dimension - 8; x < dimension; x++) + AppendBit(&mut formatInfoBits2, getBit(bitMatrix, x, 8, None)); + } + + return Ok(FormatInformation::DecodeQR( + formatInfoBits1 as u32, + formatInfoBits2 as u32, + )); +} + +pub fn ReadQRCodewords( + bitMatrix: &BitMatrix, + version: VersionRef, + formatInfo: &FormatInformation, +) -> Result> { + let functionPattern: BitMatrix = version.buildFunctionPattern()?; + + let mut result = Vec::new(); + result.reserve(version.getTotalCodewords() as usize); + let mut currentByte = 0; + let mut readingUp = true; + let mut bitsRead = 0; + let dimension = bitMatrix.height(); + // Read columns in pairs, from right to left + let mut x = (dimension as i32) - 1; + while x > 0 { + // for (int x = dimension - 1; x > 0; x -= 2) { + // Skip whole column with vertical timing pattern. + if (x == 6) { + x -= 1; + } + // Read alternatingly from bottom to top then top to bottom + for row in 0..dimension { + // for (int row = 0; row < dimension; row++) { + let y = if readingUp { dimension - 1 - row } else { row }; + for col in 0..2 { + // for (int col = 0; col < 2; col++) { + let xx = (x - col) as u32; + // Ignore bits covered by the function pattern + if (!functionPattern.get(xx, y)) { + // Read a bit + AppendBit( + &mut currentByte, + GetDataMaskBit(formatInfo.data_mask as u32, xx, y, None)? + != getBit(bitMatrix, xx, y, Some(formatInfo.isMirrored)), + ); + // If we've made a whole byte, save it off + bitsRead += 1; + if (bitsRead % 8 == 0) { + result.push(std::mem::take(&mut currentByte)); + } + } + } + } + readingUp = !readingUp; // switch directions + + x -= 2; + } + if ((result.len()) != version.getTotalCodewords() as usize) { + return Err(Exceptions::FORMAT); + } + + Ok(result.iter().copied().map(|x| x as u8).collect()) +} + +pub fn ReadMQRCodewords( + bitMatrix: &BitMatrix, + version: VersionRef, + formatInfo: &FormatInformation, +) -> Result> { + let functionPattern = version.buildFunctionPattern()?; + + // D3 in a Version M1 symbol, D11 in a Version M3-L symbol and D9 + // in a Version M3-M symbol is a 2x2 square 4-module block. + // See ISO 18004:2006 6.7.3. + let hasD4mBlock = version.getVersionNumber() % 2 == 1; + let d4mBlockIndex = if version.getVersionNumber() == 1 { + 3 + } else { + (if formatInfo.error_correction_level == ErrorCorrectionLevel::L { + 11 + } else { + 9 + }) + }; + + let mut result = Vec::new(); + result.reserve(version.getTotalCodewords() as usize); + let mut currentByte = 0; + let mut readingUp = true; + let mut bitsRead = 0; + let dimension = bitMatrix.height(); + // Read columns in pairs, from right to left + let mut x = dimension - 1; + while x > 0 { + // for (int x = dimension - 1; x > 0; x -= 2) { + // Read alternatingly from bottom to top then top to bottom + for row in 0..dimension { + // for (int row = 0; row < dimension; row++) { + let y = if readingUp { dimension - 1 - row } else { row }; + for col in 0..2 { + // for (int col = 0; col < 2; col++) { + let xx = x - col; + // Ignore bits covered by the function pattern + if (!functionPattern.get(xx, y)) { + // Read a bit + AppendBit( + &mut currentByte, + GetDataMaskBit(formatInfo.data_mask as u32, xx, y, Some(true))? + != getBit(bitMatrix, xx, y, Some(formatInfo.isMirrored)), + ); + bitsRead += 1; + // If we've made a whole byte, save it off; save early if 2x2 data block. + if (bitsRead == 8 + || (bitsRead == 4 && hasD4mBlock && (result.len()) == d4mBlockIndex - 1)) + { + result.push(std::mem::take(&mut currentByte)); + bitsRead = 0; + } + } + } + } + readingUp = !readingUp; // switch directions + + x -= 2; + } + if ((result.len()) != version.getTotalCodewords() as usize) { + return Err(Exceptions::FORMAT); + } + + Ok(result.iter().copied().map(|x| x as u8).collect()) +} + +pub fn ReadCodewords( + bitMatrix: &BitMatrix, + version: VersionRef, + formatInfo: &FormatInformation, +) -> Result> { + if (!hasValidDimension(bitMatrix, version.isMicroQRCode())) { + return Err(Exceptions::FORMAT); + } + + if version.isMicroQRCode() { + ReadMQRCodewords(bitMatrix, version, formatInfo) + } else { + ReadQRCodewords(bitMatrix, version, formatInfo) + } +} diff --git a/src/qrcode/cpp_port/data_mask.rs b/src/qrcode/cpp_port/data_mask.rs new file mode 100644 index 0000000..6effb85 --- /dev/null +++ b/src/qrcode/cpp_port/data_mask.rs @@ -0,0 +1,55 @@ +/* +* Copyright 2016 Nu-book Inc. +* Copyright 2016 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::BitMatrix; +use crate::common::Result; +use crate::Exceptions; + +/** +*

Encapsulates data masks for the data bits in a QR and micro QR code, per ISO 18004:2006 6.8.

+* +*

Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position +* and j is row position. In fact, as the text says, i is row position and j is column position.

+*/ + +pub fn GetDataMaskBit(maskIndex: u32, x: u32, y: u32, isMicro: Option) -> Result { + let isMicro = isMicro.unwrap_or(false); + let mut maskIndex = maskIndex; + if (isMicro) { + if (maskIndex < 0 || maskIndex >= 4) { + return Err(Exceptions::illegal_argument_with( + "QRCode maskIndex out of range", + )); + } + maskIndex = [1, 4, 6, 7][maskIndex as usize]; // map from MQR to QR indices + } + + match (maskIndex) { + 0 => return Ok((y + x) % 2 == 0), + 1 => return Ok(y % 2 == 0), + 2 => return Ok(x % 3 == 0), + 3 => return Ok((y + x) % 3 == 0), + 4 => return Ok(((y / 2) + (x / 3)) % 2 == 0), + 5 => return Ok((y * x) % 6 == 0), + 6 => return Ok(((y * x) % 6) < 3), + 7 => return Ok((y + x + ((y * x) % 3)) % 2 == 0), + _ => {} + } + + Err(Exceptions::illegal_argument_with( + "QRCode maskIndex out of range", + )) +} + +pub fn GetMaskedBit( + bits: &BitMatrix, + x: u32, + y: u32, + maskIndex: u32, + isMicro: Option, +) -> Result { + Ok(GetDataMaskBit(maskIndex, x, y, isMicro)? != bits.get(x, y)) +} diff --git a/src/qrcode/cpp_port/decoder.rs b/src/qrcode/cpp_port/decoder.rs new file mode 100644 index 0000000..79fec91 --- /dev/null +++ b/src/qrcode/cpp_port/decoder.rs @@ -0,0 +1,446 @@ +// /* +// * Copyright 2016 Nu-book Inc. +// * Copyright 2016 ZXing authors +// */ +// // SPDX-License-Identifier: Apache-2.0 + +use crate::common::cpp_essentials::{DecoderResult, StructuredAppendInfo}; +use crate::common::reedsolomon::{ + get_predefined_genericgf, PredefinedGenericGF, ReedSolomonDecoder, +}; +use crate::common::{ + AIFlag, BitMatrix, BitSource, CharacterSet, DecoderRXingResult, ECIStringBuilder, Eci, Result, + SymbologyIdentifier, +}; +use crate::qrcode::cpp_port::bitmatrix_parser::{ + ReadCodewords, ReadFormatInformation, ReadVersion, +}; +use crate::qrcode::decoder::{DataBlock, ErrorCorrectionLevel, Mode, Version}; +use crate::Exceptions; + +/** +*

Given data and error-correction codewords received, possibly corrupted by errors, attempts to +* correct the errors in-place using Reed-Solomon error correction.

+* +* @param codewordBytes data and error correction codewords +* @param numDataCodewords number of codewords that are data bytes +* @return false if error correction fails +*/ +pub fn CorrectErrors(codewordBytes: &mut [u8], numDataCodewords: u32) -> Result { + // First read into an array of ints + // std::vector codewordsInts(codewordBytes.begin(), codewordBytes.end()); + let mut codewordsInts = codewordBytes.iter().copied().map(|b| b as i32).collect(); + + let numECCodewords = ((codewordBytes.len() as u32) - numDataCodewords) as i32; + let rs = ReedSolomonDecoder::new(get_predefined_genericgf( + PredefinedGenericGF::QrCodeField256, + )); + + rs.decode(&mut codewordsInts, numECCodewords)?; + + // if rs.decode(&mut codewordsInts, numECCodewords)? != 0 + // // if (!ReedSolomonDecode(GenericGF::QRCodeField256(), codewordsInts, numECCodewords)) + // { + // return Ok(false); + // } + + // Copy back into array of bytes -- only need to worry about the bytes that were data + // We don't care about errors in the error-correction codewords + codewordBytes[..numDataCodewords as usize].copy_from_slice( + &codewordsInts[..numDataCodewords as usize] + .into_iter() + .copied() + .map(|i| i as u8) + .collect::>(), + ); + // std::copy_n(codewordsInts.begin(), numDataCodewords, codewordBytes.begin()); + + Ok(true) +} + +/** +* See specification GBT 18284-2000 +*/ +pub fn DecodeHanziSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + let mut count = count; + + // Each character will require 2 bytes, decode as GB2312 + // There is no ECI value for GB2312, use GB18030 which is a superset + result.switch_encoding(CharacterSet::GB18030, false); + result.reserve(2 * count as usize); + + while (count > 0) { + // Each 13 bits encodes a 2-byte character + let twoBytes = bits.readBits(13)?; + let mut assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060); + if (assembledTwoBytes < 0x00A00) { + // In the 0xA1A1 to 0xAAFE range + assembledTwoBytes += 0x0A1A1; + } else { + // In the 0xB0A1 to 0xFAFE range + assembledTwoBytes += 0x0A6A1; + } + *result += ((assembledTwoBytes >> 8) & 0xFF) as u8; + *result += (assembledTwoBytes & 0xFF) as u8; + count -= 1; + } + Ok(()) +} + +pub fn DecodeKanjiSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + let mut count = count; + // Each character will require 2 bytes. Read the characters as 2-byte pairs + // and decode as Shift_JIS afterwards + result.switch_encoding(CharacterSet::Shift_JIS, false); + result.reserve(2 * count as usize); + + while (count > 0) { + // Each 13 bits encodes a 2-byte character + let twoBytes = bits.readBits(13)?; + let mut assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0); + if (assembledTwoBytes < 0x01F00) { + // In the 0x8140 to 0x9FFC range + assembledTwoBytes += 0x08140; + } else { + // In the 0xE040 to 0xEBBF range + assembledTwoBytes += 0x0C140; + } + *result += (assembledTwoBytes >> 8) as u8; + *result += (assembledTwoBytes) as u8; + count -= 1; + } + Ok(()) +} + +pub fn DecodeByteSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + result.switch_encoding(CharacterSet::Unknown, false); + result.reserve(count as usize); + + for _i in 0..count { + // for (int i = 0; i < count; i++) + *result += (bits.readBits(8)?) as u8; + } + Ok(()) +} + +pub fn ToAlphaNumericChar(value: u32) -> Result { + let value = value as usize; + /** + * See ISO 18004:2006, 6.4.4 Table 5 + */ + const ALPHANUMERIC_CHARS: [char; 45] = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + ' ', '$', '%', '*', '+', '-', '.', '/', ':', + ]; + + if (value < 0 || value >= (ALPHANUMERIC_CHARS.len())) { + return Err(Exceptions::index_out_of_bounds_with( + "oAlphaNumericChar: out of range", + )); + } + + Ok(ALPHANUMERIC_CHARS[value]) +} + +pub fn DecodeAlphanumericSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + let mut count = count; + + // Read two characters at a time + let mut buffer = String::new(); + + while count > 1 { + let nextTwoCharsBits = bits.readBits(11)?; + buffer.push(ToAlphaNumericChar(nextTwoCharsBits / 45)?); + buffer.push(ToAlphaNumericChar(nextTwoCharsBits % 45)?); + count -= 2; + } + if (count == 1) { + // special case: one character left + buffer.push(ToAlphaNumericChar(bits.readBits(6)?)?); + } + // See section 6.4.8.1, 6.4.8.2 + if (result.symbology.aiFlag != AIFlag::None) { + // We need to massage the result a bit if in an FNC1 mode: + for i in 0..buffer.len() { + // for (size_t i = 0; i < buffer.length(); i++) { + if (buffer + .chars() + .nth(i) + .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)? + == '%') + { + if (i < buffer.len() - 1 + && buffer + .chars() + .nth(i + 1) + .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)? + == '%') + { + // %% is rendered as % + buffer.remove(i + 1); + // buffer.erase(i + 1); + } else { + // In alpha mode, % should be converted to FNC1 separator 0x1D + buffer.replace_range(i..i, &char::from(0x1D).to_string()); + // buffer[i] = static_cast(0x1D); + } + } + } + } + + result.switch_encoding(CharacterSet::ISO8859_1, false); + *result += buffer; + + Ok(()) +} + +pub fn DecodeNumericSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + let mut count = count; + + result.switch_encoding(CharacterSet::ISO8859_1, false); + result.reserve(count as usize); + + while (count > 0) { + let n = std::cmp::min(count, 3); + let nDigits = bits.readBits(1 + 3 * n as usize)?; // read 4, 7 or 10 bits into 1, 2 or 3 digits + result.append_string(&crate::common::cpp_essentials::util::ToString( + nDigits as usize, + n as usize, + )?); + count -= n; + } + + Ok(()) +} + +pub fn ParseECIValue(bits: &mut BitSource) -> Result { + let firstByte = bits.readBits(8)?; + if ((firstByte & 0x80) == 0) { + // just one byte + return Ok(Eci::from(firstByte & 0x7F)); + } + if ((firstByte & 0xC0) == 0x80) { + // two bytes + let secondByte = bits.readBits(8)?; + return Ok(Eci::from(((firstByte & 0x3F) << 8) | secondByte)); + } + if ((firstByte & 0xE0) == 0xC0) { + // three bytes + let secondThirdBytes = bits.readBits(16)?; + return Ok(Eci::from(((firstByte & 0x1F) << 16) | secondThirdBytes)); + } + Err(Exceptions::format_with("ParseECIValue: invalid value")) +} + +/** + * QR codes encode mode indicators and terminator codes into a constant bit length of 4. + * Micro QR codes have terminator codes that vary in bit length but are always longer than + * the mode indicators. + * M1 - 0 length mode code, 3 bits terminator code + * M2 - 1 bit mode code, 5 bits terminator code + * M3 - 2 bit mode code, 7 bits terminator code + * M4 - 3 bit mode code, 9 bits terminator code + * IsTerminator peaks into the bit stream to see if the current position is at the start of + * a terminator code. If true, then the decoding can finish. If false, then the decoding + * can read off the next mode code. + * + * See ISO 18004:2015, 7.4.1 Table 2 + * + * @param bits the stream of bits that might have a terminator code + * @param version the QR or micro QR code version + */ +pub fn IsEndOfStream(bits: &mut BitSource, version: &Version) -> Result { + let bitsRequired = Mode::get_terminator_bit_length(version); //super::qr_codec_mode::TerminatorBitsLength(version); + let bitsAvailable = std::cmp::min(bits.available(), bitsRequired as usize); + Ok(bitsAvailable == 0 || bits.peak_bits(bitsAvailable)? == 0) +} + +/** +*

QR Codes can encode text as bits in one of several modes, and can use multiple modes +* in one QR Code. This method decodes the bits back into text.

+* +*

See ISO 18004:2006, 6.4.3 - 6.4.7

+*/ +// ZXING_EXPORT_TEST_ONLY +pub fn DecodeBitStream( + bytes: &[u8], + version: &Version, + ecLevel: ErrorCorrectionLevel, +) -> Result> { + let mut bits = BitSource::new(bytes.to_vec()); + let mut result = ECIStringBuilder::default(); + // Error error; + result.symbology = SymbologyIdentifier { + code: b'Q', + modifier: b'1', + eciModifierOffset: 1, + aiFlag: AIFlag::None, + }; //{'Q', '1', 1}; + let mut structuredAppend = StructuredAppendInfo::default(); + let modeBitLength = Mode::get_codec_mode_bits_length(version); + + let res = (|| { + while !IsEndOfStream(&mut bits, version)? { + let mode: Mode; + if modeBitLength == 0 { + mode = Mode::NUMERIC; // MicroQRCode version 1 is always NUMERIC and modeBitLength is 0 + } else { + mode = Mode::CodecModeForBits( + bits.readBits(modeBitLength as usize)?, + Some(version.isMicroQRCode()), + )?; + } + + match mode { + Mode::FNC1_FIRST_POSITION => { + // if (!result.empty()) // uncomment to enforce specification + // throw FormatError("GS1 Indicator (FNC1 in first position) at illegal position"); + result.symbology.modifier = b'3'; + result.symbology.aiFlag = AIFlag::GS1; // In Alphanumeric mode undouble doubled '%' and treat single '%' as + } + Mode::FNC1_SECOND_POSITION => { + if !result.is_empty() { + return Err(Exceptions::format_with("AIM Application Indicator (FNC1 in second position) at illegal position")); + // throw FormatError("AIM Application Indicator (FNC1 in second position) at illegal position"); + } + result.symbology.modifier = b'5'; // As above + // ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position), "00-99" or "A-Za-z" + let appInd = bits.readBits(8)?; + if appInd < 100 + // "00-09" + { + result += + crate::common::cpp_essentials::util::ToString(appInd as usize, 2)?; + } else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) + // "A-Za-z" + { + result += (appInd - 100) as u8; + } else { + return Err(Exceptions::format_with("Invalid AIM Application Indicator")); + // throw FormatError("Invalid AIM Application Indicator"); + } + result.symbology.aiFlag = AIFlag::AIM; // see also above + } + Mode::STRUCTURED_APPEND => { + // sequence number and parity is added later to the result metadata + // Read next 4 bits of index, 4 bits of symbol count, and 8 bits of parity data, then continue + structuredAppend.index = bits.readBits(4)? as i32; + structuredAppend.count = bits.readBits(4)? as i32 + 1; + structuredAppend.id = (bits.readBits(8)?).to_string(); //std::to_string(bits.readBits(8)); + } + Mode::ECI => { + // Count doesn't apply to ECI + result.switch_encoding(ParseECIValue(&mut bits)?.into(), true); + } + Mode::HANZI => { + // First handle Hanzi mode which does not start with character count + // chinese mode contains a sub set indicator right after mode indicator + let subset = bits.readBits(4)?; + if (subset != 1) + // GB2312_SUBSET is the only supported one right now + { + return Err(Exceptions::format_with("Unsupported HANZI subset")); + // throw FormatError("Unsupported HANZI subset"); + } + let count = bits.readBits(mode.CharacterCountBits(version) as usize)?; + DecodeHanziSegment(&mut bits, count, &mut result)?; + } + _ => { + // "Normal" QR code modes: + // How many characters will follow, encoded in this mode? + let count = bits.readBits(mode.CharacterCountBits(version) as usize)?; + match mode { + Mode::NUMERIC => DecodeNumericSegment(&mut bits, count, &mut result)?, + Mode::ALPHANUMERIC => { + DecodeAlphanumericSegment(&mut bits, count, &mut result)? + } + Mode::BYTE => DecodeByteSegment(&mut bits, count, &mut result)?, + Mode::KANJI => DecodeKanjiSegment(&mut bits, count, &mut result)?, + _ => return Err(Exceptions::format_with("Invalid CodecMode")), //throw FormatError("Invalid CodecMode"); + }; + } + } + } + Ok(()) + })(); + + Ok(DecoderResult::with_eci_string_builder(result) + .withError(res.err()) + .withEcLevel(ecLevel.to_string()) + .withVersionNumber(version.getVersionNumber()) + .withStructuredAppend(structuredAppend)) +} + +pub fn Decode(bits: &BitMatrix) -> Result> { + let Ok(pversion) = ReadVersion(bits) else { + return Err(Exceptions::format_with("Invalid version")) + }; + let version = pversion; + + let Ok(formatInfo) = ReadFormatInformation(bits, version.isMicroQRCode()) else { + return Err(Exceptions::format_with("Invalid format information")) + }; + + // Read codewords + let codewords = ReadCodewords(bits, &version, &formatInfo)?; + if (codewords.is_empty()) { + return Err(Exceptions::format_with("Failed to read codewords")); + } + + // Separate into data blocks + let dataBlocks: Vec = + DataBlock::getDataBlocks(&codewords, &version, formatInfo.error_correction_level)?; + if (dataBlocks.is_empty()) { + return Err(Exceptions::format_with("Failed to get data blocks")); + } + + // Count total number of data bytes + let op = |totalBytes, dataBlock: &DataBlock| totalBytes + dataBlock.getNumDataCodewords(); + let totalBytes = dataBlocks.iter().fold(0, op); // std::accumulate(std::begin(dataBlocks), std::end(dataBlocks), int{}, op); + let mut resultBytes = vec![0u8; totalBytes as usize]; + let mut resultIterator = 0; //resultBytes.begin(); + + // Error-correct and copy data blocks together into a stream of bytes + for dataBlock in dataBlocks.iter() { + let mut codewordBytes = dataBlock.getCodewords().to_vec(); + let numDataCodewords = dataBlock.getNumDataCodewords() as usize; + + if !CorrectErrors(&mut codewordBytes, numDataCodewords as u32)? { + return Err(Exceptions::CHECKSUM); + } + + // resultIterator = std::copy_n(codewordBytes.begin(), numDataCodewords, resultIterator); + resultBytes[resultIterator..(resultIterator + numDataCodewords)] + .copy_from_slice(&codewordBytes[..numDataCodewords]); + resultIterator += numDataCodewords; + } + + // Decode the contents of that stream of bytes + Ok( + DecodeBitStream(&resultBytes, &version, formatInfo.error_correction_level)? + .withIsMirrored(formatInfo.isMirrored), + ) +} + +// } // namespace ZXing::QRCode diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs new file mode 100644 index 0000000..a22c832 --- /dev/null +++ b/src/qrcode/cpp_port/detector.rs @@ -0,0 +1,1060 @@ +use crate::{ + common::{ + cpp_essentials::{ + CenterOfRing, DMRegressionLine, FindConcentricPatternCorners, FindLeftGuardBy, Matrix, + }, + DefaultGridSampler, GridSampler, Result, SamplerControl, + }, + dimension, point_g, point_i, + qrcode::{ + decoder::{FormatInformation, Version, VersionRef}, + detector::QRCodeDetectorResult, + }, + Exceptions, +}; +use multimap::MultiMap; + +use crate::{ + common::{ + cpp_essentials::{ + BitMatrixCursorTrait, ConcentricPattern, Direction, EdgeTracer, FindLeftGuard, + FixedPattern, GetPatternRow, GetPatternRowTP, IsPattern, LocateConcentricPattern, + PatternRow, PatternType, PatternView, ReadSymmetricPattern, RegressionLine, + RegressionLineTrait, + }, + BitMatrix, PerspectiveTransform, Quadrilateral, + }, + point, Point, +}; + +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] +pub struct FinderPatternSet { + pub bl: ConcentricPattern, + pub tl: ConcentricPattern, + pub tr: ConcentricPattern, +} + +pub type FinderPatterns = Vec; +pub type FinderPatternSets = Vec; + +const LEN: usize = 5; +const SUM: usize = 7; +const PATTERN: FixedPattern = FixedPattern::new([1, 1, 3, 1, 1]); +const E2E: bool = true; + +fn FindPattern<'a>(view: PatternView<'a>) -> Result> { + FindLeftGuardBy::( + view, + LEN, + |view: &PatternView, spaceInPixel: Option| { + // perform a fast plausability test for 1:1:3:1:1 pattern + if (view[2] < 2 as PatternType * std::cmp::max(view[0], view[4]) + || view[2] < std::cmp::max(view[1], view[3])) + { + return false; + } + IsPattern::(view, &PATTERN, spaceInPixel, 0.5, 0.0) != 0.0 + }, + ) +} + +/// Locate the finder patterns for the symbol. +/// This function can panic +pub fn FindFinderPatterns(image: &BitMatrix, tryHarder: bool) -> FinderPatterns { + const MIN_SKIP: u32 = 3; // 1 pixel/module times 3 modules/center + const MAX_MODULES_FAST: u32 = 20 * 4 + 17; // support up to version 20 for mobile clients + + // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the + // image, and then account for the center being 3 modules in size. This gives the smallest + // number of pixels the center could be, so skip this often. When trying harder, look for all + // QR versions regardless of how dense they are. + let height = image.height(); + let mut skip = (3 * height) / (4 * MAX_MODULES_FAST); + if (skip < MIN_SKIP || tryHarder) { + skip = MIN_SKIP; + } + + let mut res: Vec = Vec::new(); + let mut y = skip - 1; + + while y < height { + // for (int y = skip - 1; y < height; y += skip) { + let mut row = PatternRow::default(); + GetPatternRowTP(image, y, &mut row, false); + let mut next: PatternView = PatternView::new(&row); + + while { + if let Ok(up_next) = FindPattern(next) { + next = up_next; + next.isValid() + } else { + false + } + } { + let p = point( + next.pixelsInFront() as f32 + + next[0] as f32 + + next[1] as f32 + + next[2] as f32 / 2.0, + y as f32 + 0.5, + ); + + // make sure p is not 'inside' an already found pattern area + if res + .iter() + .find(|old| Point::distance(p, old.p) < (old.size as f32) / 2.0) + .is_none() + { + // if (FindIf(res, [p](const auto& old) { return distance(p, old) < old.size / 2; }) == res.end()) { + let pattern = LocateConcentricPattern::( + image, + &PATTERN.into(), + p, + next.iter().sum::() as i32 * 3, + ); // 3 for very skewed samples + // Reduce(next) * 3); // 3 for very skewed samples + if (pattern.is_some()) { + // log(*pattern, 3); + // assert!(image.get_point(pattern.as_ref().unwrap().p)); + res.push(pattern.unwrap()); + } + } + + next.skipPair(); + next.skipPair(); + next.extend(); + } + + y += skip; + } + + res +} + +/** + * @brief GenerateFinderPatternSets + * @param patterns list of ConcentricPattern objects, i.e. found finder pattern squares + * @return list of plausible finder pattern sets, sorted by decreasing plausibility + */ +pub fn GenerateFinderPatternSets(patterns: &mut FinderPatterns) -> FinderPatternSets { + patterns.sort_by_key(|p| p.size); + // std::sort(patterns.begin(), patterns.end(), [](const auto& a, const auto& b) { return a.size < b.size; }); + + let mut sets: MultiMap = MultiMap::new(); + let squaredDistance = |a: ConcentricPattern, b: ConcentricPattern| { + // The scaling of the distance by the b/a size ratio is a very coarse compensation for the shortening effect of + // the camera projection on slanted symbols. The fact that the size of the finder pattern is proportional to the + // distance from the camera is used here. This approximation only works if a < b < 2*a (see below). + // Test image: fix-finderpattern-order.jpg + ConcentricPattern::dot((a - b), (a - b)) as f64 + * (((b).size as f64) / ((a).size as f64)).powi(2) //std::pow(double(b.size) / a.size, 2) + }; + + let cosUpper: f64 = (45.0_f64 / 180.0 * 3.1415).cos(); // TODO: use c++20 std::numbers::pi_v + let cosLower: f64 = (135.0_f64 / 180.0 * 3.1415).cos(); + + let nbPatterns = (patterns).len(); + + if nbPatterns < 2 { + return FinderPatternSets::default(); + } + + for i in 0..(nbPatterns - 2) { + // for (int i = 0; i < nbPatterns - 2; i++) { + for j in (i + 1)..(nbPatterns - 1) { + // for (int j = i + 1; j < nbPatterns - 1; j++) { + for k in (j + 1)..(nbPatterns - 0) { + // for (int k = j + 1; k < nbPatterns - 0; k++) { + let mut a = &patterns[i]; + let mut b = &patterns[j]; + let mut c = &patterns[k]; + // if the pattern sizes are too different to be part of the same symbol, skip this + // and the rest of the innermost loop (sorted list) + if (c.size > a.size * 2) { + break; + } + + // Orders the three points in an order [A,B,C] such that AB is less than AC + // and BC is less than AC, and the angle between BC and BA is less than 180 degrees. + + let mut distAB2 = squaredDistance(*a, *b); + let mut distBC2 = squaredDistance(*b, *c); + let mut distAC2 = squaredDistance(*a, *c); + + if (distBC2 >= distAB2 && distBC2 >= distAC2) { + std::mem::swap(&mut a, &mut b); + std::mem::swap(&mut distBC2, &mut distAC2); + } else if (distAB2 >= distAC2 && distAB2 >= distBC2) { + std::mem::swap(&mut b, &mut c); + std::mem::swap(&mut distAB2, &mut distAC2); + } + + let distAB = (distAB2.sqrt()); + let distBC = (distBC2).sqrt(); + + // Make sure distAB and distBC don't differ more than reasonable + // TODO: make sure the constant 2 is not to conservative for reasonably tilted symbols + if (distAB > 2.0 * distBC || distBC > 2.0 * distAB) { + continue; + } + + // Estimate the module count and ignore this set if it can not result in a valid decoding + let moduleCount = (distAB + distBC) + / (2.0 * (a.size + b.size + c.size) as f64 / (3.0 * 7.0)) + + 7.0; + if (moduleCount < 21.0 * 0.9 || moduleCount > 177.0 * 1.5) + // moduleCount may be overestimated, see above + { + continue; + } + + // Make sure the angle between AB and BC does not deviate from 90° by more than 45° + let cosAB_BC = (distAB2 + distBC2 - distAC2) / (2.0 * distAB * distBC); + if ((cosAB_BC.is_nan()) || cosAB_BC > cosUpper || cosAB_BC < cosLower) { + continue; + } + + // a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle). + // Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0, + // we need to check both two equal sides separately. + // The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity + // from isosceles right triangle. + let d: f64 = ((distAC2 - 2.0 * distAB2).abs() + (distAC2 - 2.0 * distBC2).abs()); + + // Use cross product to figure out whether A and C are correct or flipped. + // This asks whether BC x BA has a positive z component, which is the arrangement + // we want for A, B, C. If it's negative then swap A and C. + if (ConcentricPattern::cross(*c - *b, *a - *b) < 0.0) { + std::mem::swap(&mut a, &mut c); + } + + // arbitrarily limit the number of potential sets + // (this has performance implications while limiting the maximal number of detected symbols) + sets.insert( + d.to_string(), + FinderPatternSet { + bl: *a, + tl: *b, + tr: *c, + }, + ); + // const setSizeLimit : usize = 256; + // if (sets.len() < setSizeLimit || sets.crbegin().first > d) { + // sets.emplace(d, FinderPatternSet{a, b, c}); + // if (sets.len() > setSizeLimit) + // {sets.erase(std::prev(sets.end()));} + // } + } + } + } + + // convert from multimap to vector + let mut res: FinderPatternSets = Vec::with_capacity(sets.len()); + + for (_, v) in sets { + // for (auto& [d, s] : sets) + res.extend(v); + } + + res.sort_by_key(|i| i.bl.size); + + res +} + +pub fn EstimateModuleSize(image: &BitMatrix, a: ConcentricPattern, b: ConcentricPattern) -> f64 { + let mut cur = EdgeTracer::new(image, a.p, b.p - a.p); + if !cur.isBlack() { + return -1.0; + } + assert!(cur.isBlack()); + + let pattern = ReadSymmetricPattern::<5, _>(&mut cur, a.size * 2); + + if pattern.is_none() { + return -1.0; + } + + let pattern = pattern.unwrap(); + + if (!(IsPattern::( + &PatternView::new(&PatternRow::new(pattern.to_vec())), + &PATTERN, + None, + 0.0, + 0.0, + ) != 0.0)) + { + return -1.0; + } + + (2 * pattern.iter().sum::() - pattern[0] - pattern[4]) as f64 / 12.0 + * cur.d().length() as f64 + // (2 * Reduce(*pattern) - (*pattern)[0] - (*pattern)[4]) / 12.0 * length(cur.d) +} + +pub struct DimensionEstimate { + dim: i32, + ms: f64, + err: i32, +} + +impl Default for DimensionEstimate { + fn default() -> Self { + Self { + dim: 0, + ms: 0.0, + err: 4, + } + } +} + +pub fn EstimateDimension( + image: &BitMatrix, + a: ConcentricPattern, + b: ConcentricPattern, +) -> DimensionEstimate { + let ms_a = EstimateModuleSize(image, a, b); + let ms_b = EstimateModuleSize(image, b, a); + + if (ms_a < 0.0 || ms_b < 0.0) { + return DimensionEstimate::default(); + } + + let moduleSize = (ms_a + ms_b) / 2.0; + + let dimension = ((ConcentricPattern::distance(a, b) as f64 / moduleSize).round() as i32 + 7); + let error = 1 - (dimension % 4); + + DimensionEstimate { + dim: dimension + error, + ms: moduleSize, + err: (error).abs(), + } +} + +/// This function can panic +pub fn TraceLine(image: &BitMatrix, p: Point, d: Point, edge: i32) -> impl RegressionLineTrait { + let mut cur = EdgeTracer::new(image, p, d - p); + let mut line = RegressionLine::default(); + line.setDirectionInward(cur.back()); + + // collect points inside the black line -> backup on 3rd edge + cur.stepToEdge(Some(edge), Some(0), Some(edge == 3)); + if (edge == 3) { + cur.turnBack(); + } + + let mut curI = EdgeTracer::new(image, (cur.p), (Point::mainDirection(cur.d()))); + // make sure curI positioned such that the white->black edge is directly behind + // Test image: fix-traceline.jpg + while (!bool::from(curI.edgeAtBack())) { + if (curI.edgeAtLeft().into()) { + curI.turnRight(); + } else if (curI.edgeAtRight().into()) { + curI.turnLeft(); + } else { + curI.step(Some(-1.0)); + } + } + + for dir in [Direction::Left, Direction::Right] { + // for (auto dir : {Direction::LEFT, Direction::RIGHT}) { + let mut c = EdgeTracer::new(image, curI.p, curI.direction(dir)); + let mut stepCount = (Point::maxAbsComponent(cur.p - p)) as i32; + loop { + line.add(Point::centered(c.p)) + .expect("could not add point on line"); + + stepCount -= 1; + if !(stepCount > 0 && c.stepAlongEdge(dir, Some(true))) { + break; + } + } //while (--stepCount > 0 && c.stepAlongEdge(dir, true)); + } + + line.evaluate_max_distance(Some(1.0), Some(true)); + + line +} + +// estimate how tilted the symbol is (return value between 1 and 2, see also above) +pub fn EstimateTilt(fp: &FinderPatternSet) -> f64 { + let min = [fp.bl.size, fp.tl.size, fp.tr.size] + .iter() + .min() + .copied() + .unwrap_or(i32::MAX); + let max = [fp.bl.size, fp.tl.size, fp.tr.size] + .iter() + .max() + .copied() + .unwrap_or(i32::MIN); + + (max as f64) / (min as f64) +} + +pub fn Mod2Pix( + dimension: i32, + brOffset: Point, + pix: Quadrilateral, +) -> Result { + let mut quad = Quadrilateral::rectangle(dimension, dimension, Some(3.5)); + // let quad = Rectangle(dimension, dimension, 3.5); + quad[2] = quad[2] - brOffset; + + PerspectiveTransform::quadrilateralToQuadrilateral(quad, pix) + // return {quad, pix}; +} + +pub fn LocateAlignmentPattern( + image: &BitMatrix, + moduleSize: i32, + estimate: Point, +) -> Option { + // log(estimate, 2); + + for d in [ + point(0.0, 0.0), + point(0.0, -1.0), + point(0.0, 1.0), + point(-1.0, 0.0), + point(1.0, 0.0), + point(-1.0, -1.0), + point(1.0, -1.0), + point(1.0, 1.0), + point(-1.0, 1.0), + ] { + // for (auto d : {PointF{0, 0}, {0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}, + // #if 1 + // }) { + // #else + // {0, -2}, {0, 2}, {-2, 0}, {2, 0}, {-1, -2}, {1, -2}, {-1, 2}, {1, 2}, {-2, -1}, {-2, 1}, {2, -1}, {2, 1}}) { + // #endif + let cor = CenterOfRing( + image, + (estimate + moduleSize as f32 * 2.25 * d).floor(), + moduleSize * 3, + 1, + false, + ); + + // if we did not land on a black pixel the concentric pattern finder will fail + if cor.is_none() || !image.get_point(cor.unwrap()) { + continue; + } + + if let Some(cor1) = CenterOfRing(image, cor.unwrap().floor(), moduleSize, 1, true) { + if let Some(cor2) = CenterOfRing(image, cor.unwrap().floor(), moduleSize * 3, -2, true) + { + if Point::distance(cor1, cor2) < moduleSize as f32 / 2.0 { + let res = (cor1 + cor2) / 2.0; + // log(res, 3); + return Some(res); + } + } + } + } + + None +} + +pub fn ReadVersion( + image: &BitMatrix, + dimension: u32, + mod2Pix: PerspectiveTransform, +) -> Result { + let mut bits = [0; 2]; // + + for mirror in [false, true] { + // Read top-right/bottom-left version info: 3 wide by 6 tall (depending on mirrored) + let mut versionBits = 0; + for y in (0..=5).rev() { + // for (int y = 5; y >= 0; --y) + for x in ((dimension - 11)..=(dimension - 9)).rev() { + // for (int x = dimension - 9; x >= dimension - 11; --x) { + let mod_ = if mirror { point_i(y, x) } else { point_i(x, y) }; + let pix = mod2Pix.transform_point((mod_).centered()); + if !image.is_in(pix) { + versionBits = -1; + } else { + AppendBit(&mut versionBits, image.get_point(pix)); + } + // log(pix, 3); + } + bits[usize::from(mirror)] = versionBits; + } + } + + Version::DecodeVersionInformation(bits[0], bits[1]) +} + +pub fn AppendBit(val: &mut i32, bit: bool) { + *val <<= 1; + + *val |= i32::from(bit) +} + +pub fn SampleQR(image: &BitMatrix, fp: &FinderPatternSet) -> Result { + let top = EstimateDimension(image, fp.tl, fp.tr); + let left = EstimateDimension(image, fp.tl, fp.bl); + + if (!(top.dim != 0) && !(left.dim != 0)) { + return Err(Exceptions::NOT_FOUND); + } + + let best = if top.err == left.err { + (if top.dim > left.dim { top } else { left }) + } else { + (if top.err < left.err { top } else { left }) + }; + let mut dimension = best.dim; + let moduleSize = (best.ms + 1.0) as i32; + + let mut br = ConcentricPattern { + p: point(-1.0, -1.0), + size: 0, + }; + let mut brOffset = point_i(3, 3); + + // Everything except version 1 (21 modules) has an alignment pattern. Estimate the center of that by intersecting + // line extensions of the 1 module wide square around the finder patterns. This could also help with detecting + // slanted symbols of version 1. + + // generate 4 lines: outer and inner edge of the 1 module wide black line between the two outer and the inner + // (tl) finder pattern + let bl2 = TraceLine(image, fp.bl.p, fp.tl.p, 2); + let bl3 = TraceLine(image, fp.bl.p, fp.tl.p, 3); + let tr2 = TraceLine(image, fp.tr.p, fp.tl.p, 2); + let tr3 = TraceLine(image, fp.tr.p, fp.tl.p, 3); + + if (bl2.isValid() && tr2.isValid() && bl3.isValid() && tr3.isValid()) { + // intersect both outer and inner line pairs and take the center point between the two intersection points + let brInter = (DMRegressionLine::intersect(&bl2, &tr2).ok_or(Exceptions::NOT_FOUND)? + + DMRegressionLine::intersect(&bl3, &tr3).ok_or(Exceptions::NOT_FOUND)?) + / 2.0; + // log(brInter, 3); + + if (dimension > 21) { + if let Some(brCP) = LocateAlignmentPattern(image, moduleSize, brInter) { + br = brCP.into(); + } + } + + // if the symbol is tilted or the resolution of the RegressionLines is sufficient, use their intersection + // as the best estimate (see discussion in #199 and test image estimate-tilt.jpg ) + if (!image.is_in(br.p) + && (EstimateTilt(fp) > 1.1 + || (bl2.isHighRes() && bl3.isHighRes() && tr2.isHighRes() && tr3.isHighRes()))) + { + br = brInter.into(); + } + } + + // otherwise the simple estimation used by upstream is used as a best guess fallback + if (!image.is_in(br.p)) { + br = fp.tr - fp.tl + fp.bl; + brOffset = point_i(0, 0); + } + + // log(br, 3); + let mut mod2Pix = Mod2Pix( + dimension, + brOffset, + Quadrilateral::from([fp.tl.p, fp.tr.p, br.p, fp.bl.p]), + )?; + + if (dimension >= Version::DimensionOfVersion(7, false) as i32) { + let version = ReadVersion(image, dimension as u32, mod2Pix.clone()); + + // if the version bits are garbage -> discard the detection + if (!version.is_ok() + || (version.as_ref().unwrap().getDimensionForVersion() as i32 - dimension).abs() > 8) + { + /*return DetectorResult();*/ + return Err(Exceptions::NOT_FOUND); + } + if (version.as_ref().unwrap().getDimensionForVersion() as i32 != dimension) { + // printf("update dimension: %d -> %d\n", dimension, version.dimension()); + dimension = version.as_ref().unwrap().getDimensionForVersion() as i32; + mod2Pix = Mod2Pix( + dimension, + brOffset, + Quadrilateral::from([fp.tl.p, fp.tr.p, br.p, fp.bl.p]), + )?; + } + // #if 1 + let apM = version.as_ref().unwrap().getAlignmentPatternCenters(); // alignment pattern positions in modules + let mut apP = Matrix::new(apM.len(), apM.len())?; // found/guessed alignment pattern positions in pixels + // let apP = Matrix>(Size(apM), Size(apM)); // found/guessed alignment pattern positions in pixels + let N = (apM.len()) - 1; + + // project the alignment pattern at module coordinates x/y to pixel coordinate based on current mod2Pix + let projectM2P = /*[&mod2Pix, &apM]*/| x, y, mod2Pix: &PerspectiveTransform| { mod2Pix.transform_point(Point::centered(point_i(apM[x], apM[y]))) }; + + let mut findInnerCornerOfConcentricPattern = /*[&image, &apP, &projectM2P]*/| x, y, fp:ConcentricPattern| { + let pc = apP.set(x, y, projectM2P(x, y, &mod2Pix)); + if let Some(fpQuad) = FindConcentricPatternCorners(image, fp.p, fp.size, 2) + // if (auto fpQuad = FindConcentricPatternCorners(image, fp, fp.size, 2)) + {for c in fpQuad .0 + {if Point::distance(c, pc) < (fp.size as f32) / 2.0 + {apP.set(x, y, c);}}} + }; + + findInnerCornerOfConcentricPattern(0, 0, fp.tl); + findInnerCornerOfConcentricPattern(0, N, fp.bl); + findInnerCornerOfConcentricPattern(N, 0, fp.tr); + + let bestGuessAPP = |x, y, apP: &Matrix| { + if let Some(p) = apP.get(x, y) + // if (auto p = apP(x, y)) + { + return p; + } + return projectM2P(x, y, &mod2Pix); + }; + + for y in 0..=N { + // for (int y = 0; y <= N; ++y) + for x in 0..=N { + // for (int x = 0; x <= N; ++x) { + if (apP.get(x, y).is_some()) { + continue; + } + + let guessed = if x * y == 0 { + bestGuessAPP(x, y, &apP) + } else { + bestGuessAPP(x - 1, y, &apP) + bestGuessAPP(x, y - 1, &apP) + - bestGuessAPP(x - 1, y - 1, &apP) + }; + if let Some(found) = LocateAlignmentPattern(image, moduleSize, guessed) + // if (auto found = LocateAlignmentPattern(image, moduleSize, guessed)) + { + apP.set(x, y, found); + } + } + } + + // go over the whole set of alignment patters again and try to fill any remaining gap by using available neighbors as guides + for y in 0..=N { + // for (int y = 0; y <= N; ++y) { + for x in 0..=N { + // for (int x = 0; x <= N; ++x) { + if (apP.get(x, y).is_some()) { + continue; + } + + // find the two closest valid alignment pattern pixel positions both horizontally and vertically + let mut hori = Vec::new(); + let mut verti = Vec::new(); + let mut i = 2; + while i < 2 * N + 2 && hori.len() < 2 { + let xi = x as isize + i as isize / 2 * (if i % 2 != 0 { 1 } else { -1 }); + if (0 <= xi && xi <= N as isize && apP.get(xi as usize, y).is_some()) { + hori.push( + apP.get(xi as usize, y) + .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?, + ); + } + i += 1; + } + // for (int i = 2; i < 2 * N + 2 && Size(hori) < 2; ++i) { + // let xi = x + i / 2 * (i%2 ? 1 : -1); + // if (0 <= xi && xi <= N && apP(xi, y)) + // {hori.push_back(*apP(xi, y));} + // } + let mut i = 2; + while i < 2 * N + 2 && verti.len() < 2 { + let yi = y as isize + i as isize / 2 * (if i % 2 != 0 { 1 } else { -1 }); + if (0 <= yi && yi <= N as isize && apP.get(x, yi as usize).is_some()) { + verti.push( + apP.get(x, yi as usize) + .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?, + ); + } + i += 1; + } + // for (int i = 2; i < 2 * N + 2 && Size(verti) < 2; ++i) { + // let yi = y + i / 2 * (i%2 ? 1 : -1); + // if (0 <= yi && yi <= N && apP(x, yi)) + // {verti.push_back(*apP(x, yi));} + // } + + // if we found 2 each, intersect the two lines that are formed by connecting the point pairs + if ((hori.len()) == 2 && (verti.len()) == 2) { + let guessed = RegressionLine::intersect( + &DMRegressionLine::new(hori[0], hori[1]), + &DMRegressionLine::new(verti[0], verti[1]), + ) + .ok_or(Exceptions::ILLEGAL_STATE)?; + let found = LocateAlignmentPattern(image, moduleSize, guessed); + // search again near that intersection and if the search fails, use the intersection + // if (!found.is_some()) {printf("location guessed at %dx%d\n", x, y)}; + apP.set( + x, + y, + if found.is_some() { + found.unwrap() + } else { + guessed + }, + ); + } + } + } + + if let Some(c) = apP.get(N, N) + // if (auto c = apP.get(N, N)) + { + mod2Pix = Mod2Pix( + dimension, + point_i(3, 3), + Quadrilateral::from([fp.tl.p, fp.tr.p, c, fp.bl.p]), + )?; + } + + // go over the whole set of alignment patters again and fill any remaining gaps by a projection based on an updated mod2Pix + // projection. This works if the symbol is flat, wich is a reasonable fall-back assumption. + for y in 0..=N { + // for (int y = 0; y <= N; ++y) { + for x in 0..=N { + // for (int x = 0; x <= N; ++x) { + if (apP.get(x, y).is_some()) { + continue; + } + + // printf("locate failed at %dx%d\n", x, y); + apP.set(x, y, projectM2P(x, y, &mod2Pix)); + } + } + + // assemble a list of region-of-interests based on the found alignment pattern pixel positions + + let mut rois = Vec::new(); + for y in 0..N { + // for (int y = 0; y < N; ++y){ + for x in 0..N { + // for (int x = 0; x < N; ++x) { + let x0 = apM[x]; + let x1 = apM[x + 1]; + let y0 = apM[y]; + let y1 = apM[y + 1]; + rois.push(SamplerControl { + p0: point_i(x0 - u32::from(x == 0) * 6, y0 - u32::from(y == 0) * 6), + p1: point_i( + x1 + u32::from(x == N - 1) * 7, + y1 + u32::from(y == N - 1) * 7, + ), + transform: PerspectiveTransform::quadrilateralToQuadrilateral( + Quadrilateral::rectangle_from_xy( + x0 as f32, x1 as f32, y0 as f32, y1 as f32, None, + ), + Quadrilateral::from([ + apP.get(x, y).unwrap(), + apP.get(x + 1, y).unwrap(), + apP.get(x + 1, y + 1).unwrap(), + apP.get(x, y + 1).unwrap(), + ]), + )?, + }); + } + } + let grid_sampler = DefaultGridSampler::default(); + let (sampled, rp) = + grid_sampler.sample_grid(image, dimension as u32, dimension as u32, &rois)?; + let result = QRCodeDetectorResult::new(sampled, rp.to_vec()); + return Ok(result); + // grid_sampler.sample_grid(image, dimension, dimension, &rois); + // #endif + } + + let grid_sampler = DefaultGridSampler::default(); + let (sampled, rps) = grid_sampler.sample_grid( + image, + dimension as u32, + dimension as u32, + &[SamplerControl { + p1: point_i(dimension as u32, dimension as u32), + p0: point_i(0, 0), + transform: mod2Pix, + }], + )?; + let result = QRCodeDetectorResult::new(sampled, rps.to_vec()); + Ok(result) + // return SampleGrid(image, dimension, dimension, mod2Pix); +} + +/** +* This method detects a code in a "pure" image -- that is, pure monochrome image +* which contains only an unrotated, unskewed, image of a code, with some white border +* around it. This is a specialized method that works exceptionally fast in this special +* case. +*/ +pub fn DetectPureQR(image: &BitMatrix) -> Result { + type Pattern = [PatternType; 5]; + + // #ifdef PRINT_DEBUG + // SaveAsPBM(image, "weg.pbm"); + // #endif + + let MIN_MODULES: u32 = Version::DimensionOfVersion(1, false); + let MAX_MODULES: u32 = Version::DimensionOfVersion(40, false); + + let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES); + + if (!found || (width as i32 - height as i32).abs() > 1) { + return Err(Exceptions::NOT_FOUND); + } + let right = left + width - 1; + let bottom = top + height - 1; + + let tl = point_i(left, top); + let tr = point_i(right, top); + let bl = point_i(left, bottom); + let mut diagonal: Pattern = Default::default(); + // allow corners be moved one pixel inside to accommodate for possible aliasing artifacts + for [p, d] in [ + [tl, point_i(1, 1)], + [tr, point(-1.0, 1.0)], + [bl, point(1.0, -1.0)], + ] { + // for (auto [p, d] : {std::pair(tl, PointI{1, 1}), {tr, {-1, 1}}, {bl, {1, -1}}}) { + diagonal = EdgeTracer::new(image, p, d) + .readPatternFromBlack(1, Some((width / 3 + 1) as i32)) + .ok_or(Exceptions::NOT_FOUND)?; + // diagonal = BitMatrixCursorI(image, p, d).readPatternFromBlack(1, width / 3 + 1); + let diag_hld = diagonal.to_vec().into(); + let view = PatternView::new(&diag_hld); + if (!(IsPattern::(&view, &PATTERN, None, 0.0, 0.0) != 0.0)) { + return Err(Exceptions::NOT_FOUND); + } + } + + let fpWidth = diagonal.iter().sum::() as i32; //Reduce(diagonal); + let dimension = EstimateDimension( + image, + ConcentricPattern { + p: tl + fpWidth as f32 / 2.0 * point_i(1, 1), + size: fpWidth, + }, + ConcentricPattern { + p: tr + fpWidth as f32 / 2.0 * point(-1.0, 1.0), + size: fpWidth, + }, + ) + .dim; + + let moduleSize: f32 = ((width) as f32) / dimension as f32; + if (dimension < MIN_MODULES as i32 + || dimension > MAX_MODULES as i32 + || !image.is_in(point( + left as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize as f32, + top as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize, + ))) + { + return Err(Exceptions::NOT_FOUND); + } + + // #ifdef PRINT_DEBUG + // LogMatrix log; + // LogMatrixWriter lmw(log, image, 5, "grid2.pnm"); + // for (int y = 0; y < dimension; y++) + // for (int x = 0; x < dimension; x++) + // log(PointF(left + (x + .5f) * moduleSize, top + (y + .5f) * moduleSize)); + // #endif + + // Now just read off the bits (this is a crop + subsample) + Ok(QRCodeDetectorResult::new( + image.Deflate( + dimension as u32, + dimension as u32, + top as f32 + moduleSize / 2.0, + left as f32 + moduleSize / 2.0, + moduleSize, + )?, + vec![ + point_i(left, top), + point_i(right, top), + point_i(right, bottom), + point_i(left, bottom), + ], + )) + + // return {Deflate(image, dimension, dimension, top + moduleSize / 2, left + moduleSize / 2, moduleSize), + // {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; +} + +pub fn DetectPureMQR(image: &BitMatrix) -> Result { + type Pattern = [PatternType; 5]; + + let MIN_MODULES = Version::DimensionOfVersion(1, true); + let MAX_MODULES = Version::DimensionOfVersion(4, true); + + let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES); + + // int left, top, width, height; + if (!found || (width as i32 - height as i32).abs() > 1) { + return Err(Exceptions::NOT_FOUND); + } + let right = left + width - 1; + let bottom = top + height - 1; + + // allow corners be moved one pixel inside to accommodate for possible aliasing artifacts + let diagonal: Pattern = EdgeTracer::new(&image, point_i(left, top), point_i(1, 1)) + .readPatternFromBlack(1, None) + .ok_or(Exceptions::ILLEGAL_STATE)?; + let diag_hld = diagonal.to_vec().into(); + let view = PatternView::new(&diag_hld); + if (!(IsPattern::(&view, &PATTERN, None, 0.0, 0.0) != 0.0)) { + return Err(Exceptions::NOT_FOUND); + } + + let fpWidth = (diagonal.into_iter().sum::()); + let moduleSize: f32 = (fpWidth as f32) / 7.0; + let dimension = (width as f32 / moduleSize).floor() as u32; + + if (dimension < MIN_MODULES + || dimension > MAX_MODULES + || !image.is_in(point( + left as f32 + moduleSize as f32 / 2.0 + (dimension - 1) as f32 * moduleSize, + top as f32 + moduleSize as f32 / 2.0 + (dimension - 1) as f32 * moduleSize, + ))) + { + return Err(Exceptions::NOT_FOUND); + } + + // #ifdef PRINT_DEBUG + // LogMatrix log; + // LogMatrixWriter lmw(log, image, 5, "grid2.pnm"); + // for (int y = 0; y < dimension; y++) + // for (int x = 0; x < dimension; x++) + // log(PointF(left + (x + .5f) * moduleSize, top + (y + .5f) * moduleSize)); + // #endif + + // Now just read off the bits (this is a crop + subsample) + Ok(QRCodeDetectorResult::new( + image.Deflate( + dimension, + dimension, + top as f32 + moduleSize / 2.0, + left as f32 + moduleSize / 2.0, + moduleSize, + )?, + vec![ + point_i(left, top), + point_i(right, top), + point_i(right, bottom), + point_i(left, bottom), + ], + )) + // return {Deflate(image, dimension, dimension, top + moduleSize / 2, left + moduleSize / 2, moduleSize), + // {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; +} + +pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result { + let Some(fpQuad) = FindConcentricPatternCorners(image, fp.p, fp.size, 2) else { + return Err(Exceptions::NOT_FOUND); + }; + + let srcQuad = Quadrilateral::rectangle(7, 7, Some(0.5)); + + // #if defined(_MSVC_LANG) // TODO: see MSVC issue https://developercommunity.visualstudio.com/t/constexpr-object-is-unable-to-be-used-as/10035065 + // static + // #else + // constexpr + // #endif + let FORMAT_INFO_COORDS: [Point; 17] = [ + point_i(0, 8), + point_i(1, 8), + point_i(2, 8), + point_i(3, 8), + point_i(4, 8), + point_i(5, 8), + point_i(6, 8), + point_i(7, 8), + point_i(8, 8), + point_i(8, 7), + point_i(8, 6), + point_i(8, 5), + point_i(8, 4), + point_i(8, 3), + point_i(8, 2), + point_i(8, 1), + point_i(8, 0), + ]; + + let mut bestFI = FormatInformation::default(); + let mut bestPT = PerspectiveTransform::quadrilateralToQuadrilateral( + srcQuad, + fpQuad.rotated_corners(Some(0), None), + )?; + + for i in 0..4 { + // for (int i = 0; i < 4; ++i) { + let mod2Pix = PerspectiveTransform::quadrilateralToQuadrilateral( + srcQuad, + fpQuad.rotated_corners(Some(i), None), + )?; + + let check = |i, checkOne: bool| { + let p = mod2Pix.transform_point(Point::centered(FORMAT_INFO_COORDS[i])); + return image.is_in(p) && (!checkOne || image.get_point(p)); + }; + + // check that we see both innermost timing pattern modules + if (!check(0, true) || !check(8, false) || !check(16, true)) { + continue; + } + + let mut formatInfoBits = 0; + for i in 1..=15 + // for (int i = 1; i <= 15; ++i) + { + AppendBit( + &mut formatInfoBits, + image.get_point(mod2Pix.transform_point(Point::centered(FORMAT_INFO_COORDS[i]))), + ); + } + + let fi = FormatInformation::DecodeMQR(formatInfoBits as u32); + if (fi.hammingDistance < bestFI.hammingDistance) { + bestFI = fi; + bestPT = mod2Pix; + } + } + + if (!bestFI.isValid()) { + return Err(Exceptions::NOT_FOUND); + } + + let dim: u32 = Version::DimensionOfVersion(bestFI.microVersion, true); + + // check that we are in fact not looking at a corner of a non-micro QRCode symbol + // we accept at most 1/3rd black pixels in the quite zone (in a QRCode symbol we expect about 1/2). + let mut blackPixels = 0; + for i in 0..dim { + // for (int i = 0; i < dim; ++i) { + let px = bestPT.transform_point(Point::centered(point_i(i, dim))); + let py = bestPT.transform_point(Point::centered(point_i(dim, i))); + blackPixels += u32::from((image.is_in(px) && image.get_point(px))) + + u32::from((image.is_in(py) && image.get_point(py))); + } + if (blackPixels > 2 * dim / 3) { + return Err(Exceptions::NOT_FOUND); + } + + let grid_sampler = DefaultGridSampler::default(); + let (sample, rps) = grid_sampler.sample_grid( + image, + dim, + dim, + &[SamplerControl { + p1: point_i(dim as u32, dim as u32), + p0: point_i(0, 0), + transform: bestPT, + }], + )?; + Ok(QRCodeDetectorResult::new(sample, rps.to_vec())) + + // SampleGrid(image, dim, dim, bestPT) +} diff --git a/src/qrcode/cpp_port/mod.rs b/src/qrcode/cpp_port/mod.rs new file mode 100644 index 0000000..f18f038 --- /dev/null +++ b/src/qrcode/cpp_port/mod.rs @@ -0,0 +1,11 @@ +mod data_mask; +pub mod decoder; +pub mod detector; +mod qr_cpp_reader; + +pub use qr_cpp_reader::QrReader; + +mod bitmatrix_parser; + +#[cfg(test)] +mod test; diff --git a/src/qrcode/cpp_port/qr_cpp_reader.rs b/src/qrcode/cpp_port/qr_cpp_reader.rs new file mode 100644 index 0000000..9976203 --- /dev/null +++ b/src/qrcode/cpp_port/qr_cpp_reader.rs @@ -0,0 +1,379 @@ +/* +* Copyright 2016 Nu-book Inc. +* Copyright 2016 ZXing authors +* Copyright 2022 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +// Result Reader::decode(const BinaryBitmap& image) const +// { +// #if 1 +// if (!_hints.isPure()) +// return FirstOrDefault(decode(image, 1)); +// #endif + +// auto binImg = image.getBitMatrix(); +// if (binImg == nullptr) +// return {}; + +// DetectorResult detectorResult; +// if (_hints.hasFormat(BarcodeFormat::QRCode)) +// detectorResult = DetectPureQR(*binImg); +// if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !detectorResult.isValid()) +// detectorResult = DetectPureMQR(*binImg); + +// if (!detectorResult.isValid()) +// return {}; + +// auto decoderResult = Decode(detectorResult.bits()); +// auto position = detectorResult.position(); + +// return Result(std::move(decoderResult), std::move(position), +// detectorResult.bits().width() < 21 ? BarcodeFormat::MicroQRCode : BarcodeFormat::QRCode); +// } + +// void logFPSet(const FinderPatternSet& fps [[maybe_unused]]) +// { +// #ifdef PRINT_DEBUG +// auto drawLine = [](PointF a, PointF b) { +// int steps = maxAbsComponent(b - a); +// PointF dir = bresenhamDirection(PointF(b - a)); +// for (int i = 0; i < steps; ++i) +// log(centered(a + i * dir), 2); +// }; + +// drawLine(fps.bl, fps.tl); +// drawLine(fps.tl, fps.tr); +// drawLine(fps.tr, fps.bl); +// #endif +// } + +// Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const +// { +// auto binImg = image.getBitMatrix(); +// if (binImg == nullptr) +// return {}; + +// #ifdef PRINT_DEBUG +// LogMatrixWriter lmw(log, *binImg, 5, "qr-log.pnm"); +// #endif + +// auto allFPs = FindFinderPatterns(*binImg, _hints.tryHarder()); + +// #ifdef PRINT_DEBUG +// printf("allFPs: %d\n", Size(allFPs)); +// #endif + +// std::vector usedFPs; +// Results results; + +// if (_hints.hasFormat(BarcodeFormat::QRCode)) { +// auto allFPSets = GenerateFinderPatternSets(allFPs); +// for (const auto& fpSet : allFPSets) { +// if (Contains(usedFPs, fpSet.bl) || Contains(usedFPs, fpSet.tl) || Contains(usedFPs, fpSet.tr)) +// continue; + +// logFPSet(fpSet); + +// auto detectorResult = SampleQR(*binImg, fpSet); +// if (detectorResult.isValid()) { +// auto decoderResult = Decode(detectorResult.bits()); +// auto position = detectorResult.position(); +// if (decoderResult.isValid()) { +// usedFPs.push_back(fpSet.bl); +// usedFPs.push_back(fpSet.tl); +// usedFPs.push_back(fpSet.tr); +// } +// if (decoderResult.isValid(_hints.returnErrors())) { +// results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode); +// if (maxSymbols && Size(results) == maxSymbols) +// break; +// } +// } +// } +// } + +// if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !(maxSymbols && Size(results) == maxSymbols)) { +// for (const auto& fp : allFPs) { +// if (Contains(usedFPs, fp)) +// continue; + +// auto detectorResult = SampleMQR(*binImg, fp); +// if (detectorResult.isValid()) { +// auto decoderResult = Decode(detectorResult.bits()); +// auto position = detectorResult.position(); +// if (decoderResult.isValid(_hints.returnErrors())) { +// results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MicroQRCode); +// if (maxSymbols && Size(results) == maxSymbols) +// break; +// } + +// } +// } +// } + +// return results; +// } + +// } // namespace ZXing::QRCode + +use crate::{ + common::{cpp_essentials::ConcentricPattern, DetectorRXingResult, HybridBinarizer}, + multi::MultipleBarcodeReader, + BarcodeFormat, BinaryBitmap, DecodeHintType, DecodeHintValue, DecodingHintDictionary, + Exceptions, RXingResult, Reader, +}; + +use super::{ + decoder::Decode, + detector::{ + DetectPureMQR, DetectPureQR, FindFinderPatterns, GenerateFinderPatternSets, SampleMQR, + SampleQR, + }, +}; + +use crate::qrcode::detector::QRCodeDetectorResult as DetectorResult; + +#[derive(Default)] +pub struct QrReader; + +impl Reader for QrReader { + fn decode( + &mut self, + image: &mut crate::BinaryBitmap, + ) -> crate::common::Result { + self.decode_with_hints(image, &DecodingHintDictionary::new()) + } + + fn decode_with_hints( + &mut self, + image: &mut crate::BinaryBitmap, + hints: &crate::DecodingHintDictionary, + ) -> crate::common::Result { + // #if 1 + if !matches!( + hints.get(&DecodeHintType::PURE_BARCODE), + Some(DecodeHintValue::PureBarcode(true)) + ) + // if !matches!(Some(hints.get(&DecodeHintType::PURE_BARCODE))) + // if (!_hints.isPure()) + { + return Ok(self + .decode_set_number_with_hints(image, hints, 1)? + .first() + .ok_or(Exceptions::NOT_FOUND)? + .clone()); + // return FirstOrDefault(decode(image, 1)); + } + // #endif + + let binImg = image.get_black_matrix(); //image.getBitMatrix(); + // if (binImg == nullptr) + // {return {};} + + let mut detectorResult = Err(Exceptions::NOT_FOUND); + if let Some(DecodeHintValue::PossibleFormats(formats)) = + hints.get(&DecodeHintType::POSSIBLE_FORMATS) + { + if formats.contains(&BarcodeFormat::QR_CODE) { + detectorResult = DetectPureQR(binImg); + } + if formats.contains(&BarcodeFormat::MICRO_QR_CODE) && detectorResult.is_err() { + detectorResult = DetectPureMQR(binImg); + } + } + + if detectorResult.is_err() { + for decode_function in [DetectPureQR, DetectPureMQR] { + detectorResult = decode_function(binImg); + if detectorResult.is_ok() { + break; + } + } + } + + let detectorResult = detectorResult?; + + // let detectorResult: DetectorResult; + // if (_hints.hasFormat(BarcodeFormat::QR_CODE)) + // {detectorResult = DetectPureQR(binImg);} + + // if (_hints.hasFormat(BarcodeFormat::MICRO_QR_CODE) && !detectorResult.isValid()) + // {detectorResult = DetectPureMQR(binImg);} + + // if (!detectorResult.isValid()) + // {return {};} + + let decoderResult = Decode(detectorResult.getBits())?; + let position = detectorResult.getPoints(); + + Ok(RXingResult::with_decoder_result( + decoderResult, + position, + if detectorResult.getBits().width() < 21 { + BarcodeFormat::MICRO_QR_CODE + } else { + BarcodeFormat::QR_CODE + }, + )) + + // Ok(RXingResult::new( + // &decoderResult.content().to_string(), + // decoderResult.content().bytes().to_vec(), + // position.to_vec(), + // if detectorResult.getBits().width() < 21 { + // BarcodeFormat::MICRO_QR_CODE + // } else { + // BarcodeFormat::QR_CODE + // }, + // )) + // return Result(std::move(decoderResult), std::move(position), + // detectorResult.bits().width() < 21 ? BarcodeFormat::MICRO_QR_CODE : BarcodeFormat::QR_CODE); + } +} + +impl MultipleBarcodeReader for QrReader { + fn decode_multiple( + &mut self, + image: &mut crate::BinaryBitmap, + ) -> crate::common::Result> { + self.decode_multiple_with_hints(image, &DecodingHintDictionary::new()) + } + + fn decode_multiple_with_hints( + &mut self, + image: &mut crate::BinaryBitmap, + hints: &DecodingHintDictionary, + ) -> crate::common::Result> { + self.decode_set_number_with_hints(image, hints, u32::MAX) + } +} + +impl QrReader { + fn decode_set_number_with_hints( + &mut self, + image: &mut crate::BinaryBitmap, + hints: &DecodingHintDictionary, + count: u32, + ) -> crate::common::Result> { + let binImg = image.get_black_matrix(); //image.getBitMatrix(); + let maxSymbols = count; + // if (binImg == nullptr) + // {return {};} + + // #ifdef PRINT_DEBUG + // LogMatrixWriter lmw(log, *binImg, 5, "qr-log.pnm"); + // #endif + let try_harder = matches!( + hints.get(&DecodeHintType::TRY_HARDER), + Some(DecodeHintValue::TryHarder(true)) + ); + + let mut allFPs = FindFinderPatterns(binImg, try_harder); + + // #ifdef PRINT_DEBUG + // printf("allFPs: %d\n", Size(allFPs)); + // #endif + + let mut usedFPs: Vec = Vec::new(); + let mut results: Vec = Vec::new(); + + let (check_qr, check_mqr) = if let Some(DecodeHintValue::PossibleFormats(formats)) = + hints.get(&DecodeHintType::POSSIBLE_FORMATS) + { + ( + formats.contains(&BarcodeFormat::QR_CODE), + formats.contains(&BarcodeFormat::MICRO_QR_CODE), + ) + } else { + (true, true) + }; + + if check_qr { + // if (_hints.hasFormat(BarcodeFormat::QRCode)) { + let allFPSets = GenerateFinderPatternSets(&mut allFPs); + for fpSet in allFPSets { + // for (const auto& fpSet : allFPSets) { + if (usedFPs.contains(&fpSet.bl) + || usedFPs.contains(&fpSet.tl) + || usedFPs.contains(&fpSet.tr)) + { + continue; + } + + // logFPSet(fpSet); + + let detectorResult = SampleQR(binImg, &fpSet); + if let Ok(detectorResult) = detectorResult { + // if (detectorResult.is_ok()) { + let decoderResult = Decode(detectorResult.getBits()); + let position = detectorResult.getPoints(); + if let Ok(decoderResult) = decoderResult { + if (decoderResult.isValid()) { + usedFPs.push(fpSet.bl); + usedFPs.push(fpSet.tl); + usedFPs.push(fpSet.tr); + } + + if (decoderResult.isValid()) { + // results.push(RXingResult::new( + // &decoderResult.content().to_string(), + // decoderResult.content().bytes().to_vec(), + // position.to_vec(), + // BarcodeFormat::QR_CODE, + // )); + results.push(RXingResult::with_decoder_result( + decoderResult, + position, + BarcodeFormat::QR_CODE, + )); + // results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QR_CODE); + + if (maxSymbols != 0 && (results.len() as u32) == maxSymbols) { + break; + } + } + } + } + } + } + if (check_mqr && !(maxSymbols != 0 && (results.len() as u32) == maxSymbols)) { + // if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !(maxSymbols && Size(results) == maxSymbols)) { + for fp in allFPs { + // for (const auto& fp : allFPs) { + if (usedFPs.contains(&fp)) { + continue; + } + + let detectorResult = SampleMQR(binImg, fp); + if let Ok(detectorResult) = detectorResult { + // if (detectorResult.is_ok()) { + let decoderResult = Decode(detectorResult.getBits()); + let position = detectorResult.getPoints(); + if let Ok(decoderResult) = decoderResult { + if (decoderResult.isValid()) { + results.push(RXingResult::with_decoder_result( + decoderResult, + position, + BarcodeFormat::MICRO_QR_CODE, + )); + // results.push(RXingResult::new( + // &decoderResult.content().to_string(), + // decoderResult.content().bytes().to_vec(), + // position.to_vec(), + // BarcodeFormat::MICRO_QR_CODE, + // )); + // results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MICRO_QR_CODE); + + if (maxSymbols != 0 && (results.len() as u32) == maxSymbols) { + break; + } + } + } + } + } + } + + return Ok(results); + } +} diff --git a/src/qrcode/cpp_port/test/MQRDecoderTest.rs b/src/qrcode/cpp_port/test/MQRDecoderTest.rs new file mode 100644 index 0000000..b298150 --- /dev/null +++ b/src/qrcode/cpp_port/test/MQRDecoderTest.rs @@ -0,0 +1,125 @@ +/* + * Copyright 2017 Huy Cuong Nguyen + * Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::{common::BitMatrix, qrcode::cpp_port::decoder::Decode, Exceptions}; + +#[test] +fn MQRCodeM3L() { + const CODE_STR: &str = r"XXXXXXX X X X X +X X X X +X XXX X XXXXXXX +X XXX X X X XX +X XXX X X XX +X X X X X X +XXXXXXX X XX + X X X +XXXXXX X X X + X XX XXX +XXX XX XXXX XXX + X X XXX X +X XXXXX XXX X X + X X X XXX +XXX XX X X XXXX +"; + let bitMatrix = BitMatrix::parse_strings(CODE_STR, "X", " ").unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + assert!(result.isValid()); +} + +#[test] +fn MQRCodeM3M() { + const CODE_STR: &str = r"XXXXXXX X X X X +X X XX +X XXX X X XX XX +X XXX X X X +X XXX X XX XXXX +X X XX +XXXXXXX X XXXX + X XXX +X XX XX X X + X X XX +XX XX XXXXXXX + X X X +XX X X X + X X X +X X XXXX XXX +"; + let bitMatrix = BitMatrix::parse_strings(CODE_STR, "X", " ").unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + assert!(result.isValid()); +} + +#[test] +fn MQRCodeM1() { + const CODE_STR: &str = r"XXXXXXX X X +X X +X XXX X XXX +X XXX X XX +X XXX X X +X X XX +XXXXXXX X + X +XX X + X XXXXX X +X XXXXXX X +"; + let bitMatrix = BitMatrix::parse_strings(CODE_STR, "X", " ").unwrap(); + let result = Decode(&bitMatrix).unwrap(); + assert!(result.isValid()); + assert_eq!("123", result.text()); +} + +#[test] +fn MQRCodeM1Error4Bits() { + const CODE_STR: &str = r"XXXXXXX X X +X X XX +X XXX X X +X XXX X XX +X XXX X X +X X XX +XXXXXXX X + X +XX X + X XXXXXX +X XXXXXXX +"; + let bitMatrix = BitMatrix::parse_strings(CODE_STR, "X", " ").unwrap(); + let result = Decode(&bitMatrix); + dbg!(&result); + assert!(matches!( + result.err(), + Some(Exceptions::ReedSolomonException(_)) + )); + // assert_eq!(Error::Checksum, result.error()); + // assert!(result.text().is_empty()); +} + +#[test] +fn MQRCodeM4() { + const CODE_STR: &str = r"XXXXXXX X X X X X +X X XX X XX +X XXX X X X XX +X XXX X XX XX XX +X XXX X X XXXXX +X X XX X +XXXXXXX XX X XX + X XX XX +X X XXX X XXX + XX X XX XX X +XX XXXX X XX XX + XX XX X XX XX +XXX XXX XXX XX XX + X X X XX X +X X XX XXXXX + X X X X X +X XXXXXXX X X X +"; + let bitMatrix = BitMatrix::parse_strings(CODE_STR, "X", " ").unwrap(); + let result = Decode(&bitMatrix).unwrap(); + assert!(result.isValid()); +} diff --git a/src/qrcode/cpp_port/test/QRBitMatrixParserTest.rs b/src/qrcode/cpp_port/test/QRBitMatrixParserTest.rs new file mode 100644 index 0000000..f6610e3 --- /dev/null +++ b/src/qrcode/cpp_port/test/QRBitMatrixParserTest.rs @@ -0,0 +1,78 @@ +/* + * Copyright 2017 Huy Cuong Nguyen + * Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::BitMatrix, + qrcode::cpp_port::bitmatrix_parser::{ReadCodewords, ReadFormatInformation, ReadVersion}, +}; + +#[test] +fn MQRCodeM3L() { + let bitMatrix = BitMatrix::parse_strings( + r"XXXXXXX X X X X +X X X X +X XXX X XXXXXXX +X XXX X X X XX +X XXX X X XX +X X X X X X +XXXXXXX X XX + X X X +XXXXXX X X X + X XX XXX +XXX XX XXXX XXX + X X XXX X +X XXXXX XXX X X + X X X XXX +XXX XX X X XXXX +", + "X", + " ", + ) + .expect("parse must parse"); + + let version = ReadVersion(&bitMatrix).unwrap(); + assert_eq!(3, version.getVersionNumber()); + let format = + ReadFormatInformation(&bitMatrix, true).expect("could not read format information"); + let codewords = ReadCodewords(&bitMatrix, version, &format).expect("could not read codewords"); + assert_eq!(17, codewords.len()); + assert_eq!(0x0, codewords[10]); + assert_eq!(0xd1, codewords[11]); +} + +#[test] +fn MQRCodeM3M() { + let bitMatrix = BitMatrix::parse_strings( + r"XXXXXXX X X X X +X X XX +X XXX X X XX XX +X XXX X X X +X XXX X XX XXXX +X X XX +XXXXXXX X XXXX + X XXX +X XX XX X X + X X XX +XX XX XXXXXXX + X X X +XX X X X + X X X +X X XXXX XXX +", + "X", + " ", + ) + .unwrap(); + + let version = ReadVersion(&bitMatrix).unwrap(); + assert_eq!(3, version.getVersionNumber()); + let format = + ReadFormatInformation(&bitMatrix, true).expect("could not read format information"); + let codewords = ReadCodewords(&bitMatrix, version, &format).expect("could not read codewords"); + assert_eq!(17, codewords.len()); + assert_eq!(0x0, codewords[8]); + assert_eq!(0x89, codewords[9]); +} diff --git a/src/qrcode/cpp_port/test/QRDataMaskTest.rs b/src/qrcode/cpp_port/test/QRDataMaskTest.rs new file mode 100644 index 0000000..12e7d0b --- /dev/null +++ b/src/qrcode/cpp_port/test/QRDataMaskTest.rs @@ -0,0 +1,135 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2007 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::{common::BitMatrix, qrcode::cpp_port::data_mask::GetMaskedBit}; + +#[test] +fn Mask0() { + TestMaskAcrossDimensions(0, |i, j| { + return (i + j) % 2 == 0; + }); +} + +#[test] +fn Mask1() { + TestMaskAcrossDimensions(1, |i, _| { + return i % 2 == 0; + }); +} + +#[test] +fn Mask2() { + TestMaskAcrossDimensions(2, |_, j| { + return j % 3 == 0; + }); +} + +#[test] +fn Mask3() { + TestMaskAcrossDimensions(3, |i, j| { + return (i + j) % 3 == 0; + }); +} + +#[test] +fn Mask4() { + TestMaskAcrossDimensions(4, |i, j| { + return (i / 2 + j / 3) % 2 == 0; + }); +} + +#[test] +fn Mask5() { + TestMaskAcrossDimensions(5, |i, j| { + return (i * j) % 2 + (i * j) % 3 == 0; + }); +} + +#[test] +fn Mask6() { + TestMaskAcrossDimensions(6, |i, j| { + return ((i * j) % 2 + (i * j) % 3) % 2 == 0; + }); +} + +#[test] +fn Mask7() { + TestMaskAcrossDimensions(7, |i, j| { + return ((i + j) % 2 + (i * j) % 3) % 2 == 0; + }); +} + +#[test] +fn MicroMask0() { + TestMicroMaskAcrossDimensions(0, |i, _| { + return i % 2 == 0; + }); +} + +#[test] +fn MicroMask1() { + TestMicroMaskAcrossDimensions(1, |i, j| { + return (i / 2 + j / 3) % 2 == 0; + }); +} + +#[test] +fn MicroMask2() { + TestMicroMaskAcrossDimensions(2, |i, j| { + return ((i * j) % 2 + (i * j) % 3) % 2 == 0; + }); +} + +#[test] +fn MicroMask3() { + TestMicroMaskAcrossDimensions(3, |i, j| { + return ((i + j) % 2 + (i * j) % 3) % 2 == 0; + }); +} + +fn TestMaskAcrossDimensionsImpl( + maskIndex: u32, + isMicro: bool, + versionMax: u32, + dimensionStart: u32, + dimensionStep: u32, + condition: F, +) where + F: Fn(u32, u32) -> bool, +{ + for version in 1..=versionMax { + // for (int version = 1; version <= versionMax; version++) { + let dimension = dimensionStart + dimensionStep * version; + let bits = BitMatrix::with_single_dimension(dimension).expect("couldn't create bitmatrix"); + + for i in 0..dimension { + // for (int i = 0; i < dimension; i++) + for j in 0..dimension { + // for (int j = 0; j < dimension; j++) + assert_eq!( + GetMaskedBit(&bits, j, i, maskIndex, Some(isMicro)) + .expect("could not get mask bit"), + condition(i, j), + "({i},{j})" + ); + } + } + } +} + +fn TestMaskAcrossDimensions(maskIndex: u32, condition: F) +where + F: Fn(u32, u32) -> bool, +{ + TestMaskAcrossDimensionsImpl(maskIndex, false, 40, 17, 4, condition); +} + +fn TestMicroMaskAcrossDimensions(maskIndex: u32, condition: F) +where + F: Fn(u32, u32) -> bool, +{ + TestMaskAcrossDimensionsImpl(maskIndex, true, 4, 9, 2, condition); +} diff --git a/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs new file mode 100644 index 0000000..9cfb93f --- /dev/null +++ b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs @@ -0,0 +1,190 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +// #include "BitArray.h" +// #include "ByteArray.h" +// #include "DecoderResult.h" +// #include "qrcode/QRDataMask.h" +// #include "qrcode/QRDecoder.h" +// #include "qrcode/QRErrorCorrectionLevel.h" +// #include "qrcode/QRVersion.h" + +// #include "gtest/gtest.h" + +// namespace ZXing { +// namespace QRCode { +// DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel); +// } +// } + +// using namespace ZXing; +// using namespace ZXing::QRCode; + +use crate::common::Result; + +use crate::{ + common::{cpp_essentials::DecoderResult, BitArray}, + qrcode::{ + cpp_port::decoder::DecodeBitStream, + decoder::{ErrorCorrectionLevel, Version}, + }, +}; + +#[test] +fn SimpleByteMode() { + let mut ba = BitArray::new(); + ba.appendBits(0x04, 4).unwrap(); // Byte mode + ba.appendBits(0x03, 8).unwrap(); // 3 bytes + ba.appendBits(0xF1, 8).unwrap(); + ba.appendBits(0xF2, 8).unwrap(); + ba.appendBits(0xF3, 8).unwrap(); + let bytes: Vec = ba.into(); + let result = DecodeBitStream( + &bytes, + Version::FromNumber(1, false).expect("find_version"), + ErrorCorrectionLevel::M, + ) + .expect("Decode") + .content() + .to_string(); + let str = String::from_utf16(&[0xF1, 0xF2, 0xF3]).unwrap(); + assert_eq!(str, result); +} + +#[test] +fn SimpleSJIS() { + let mut ba = BitArray::new(); + ba.appendBits(0x04, 4).expect("append"); // Byte mode + ba.appendBits(0x04, 8).expect("append"); // 4 bytes + ba.appendBits(0xA1, 8).expect("append"); + ba.appendBits(0xA2, 8).expect("append"); + ba.appendBits(0xA3, 8).expect("append"); + ba.appendBits(0xD0, 8).expect("append"); + let bytes: Vec = ba.into(); + let result = DecodeBitStream( + &bytes, + Version::FromNumber(1, false).unwrap(), + ErrorCorrectionLevel::M, + ) + .unwrap() + .content() + .to_string(); + assert_eq!("\u{ff61}\u{ff62}\u{ff63}\u{ff90}", result); +} + +#[test] +fn ECI() { + let mut ba = BitArray::new(); + ba.appendBits(0x07, 4).expect("append"); // ECI mode + ba.appendBits(0x02, 8).expect("append"); // ECI 2 = CP437 encoding + ba.appendBits(0x04, 4).expect("append"); // Byte mode + ba.appendBits(0x03, 8).expect("append"); // 3 bytes + ba.appendBits(0xA1, 8).expect("append"); + ba.appendBits(0xA2, 8).expect("append"); + ba.appendBits(0xA3, 8).expect("append"); + let bytes: Vec = ba.into(); + let result = DecodeBitStream( + &bytes, + Version::FromNumber(1, false).unwrap(), + ErrorCorrectionLevel::M, + ) + .unwrap() + .content() + .to_string(); + assert_eq!("\u{ED}\u{F3}\u{FA}", result); +} + +#[test] +fn Hanzi() { + let mut ba = BitArray::new(); + ba.appendBits(0x0D, 4).expect("append"); // Hanzi mode + ba.appendBits(0x01, 4).expect("append"); // Subset 1 = GB2312 encoding + ba.appendBits(0x01, 8).expect("append"); // 1 characters + ba.appendBits(0x03C1, 13).expect("append"); + let bytes: Vec = ba.into(); + let result = DecodeBitStream( + &bytes, + Version::FromNumber(1, false).unwrap(), + ErrorCorrectionLevel::M, + ) + .unwrap() + .content() + .to_string(); + assert_eq!("\u{963f}", result); +} + +#[test] +fn HanziLevel1() { + let mut ba = BitArray::new(); + ba.appendBits(0x0D, 4).expect("append"); // Hanzi mode + ba.appendBits(0x01, 4).expect("append"); // Subset 1 = GB2312 encoding + ba.appendBits(0x01, 8).expect("append"); // 1 characters + // A5A2 (U+30A2) => A5A2 - A1A1 = 401, 4*60 + 01 = 0181 + ba.appendBits(0x0181, 13).expect("append"); + + let bytes: Vec = ba.into(); + + let result = DecodeBitStream( + &bytes, + Version::FromNumber(1, false).unwrap(), + ErrorCorrectionLevel::M, + ) + .unwrap() + .content() + .to_string(); + assert_eq!("\u{30a2}", result); +} + +#[test] +fn SymbologyIdentifier() { + let version = Version::FromNumber(1, false).unwrap(); + let ecLevel = ErrorCorrectionLevel::M; + + // Plain "ANUM(1) A" + let result = DecodeBitStream(&[0x20, 0x09, 0x40], version, ecLevel).unwrap(); + assert_eq!(result.symbologyIdentifier(), "]Q1"); + assert_eq!(result.text(), "A"); + + // GS1 "FNC1(1st) NUM(4) 2001" + let result = DecodeBitStream(&[0x51, 0x01, 0x0C, 0x81, 0x00], version, ecLevel).unwrap(); + assert_eq!(result.symbologyIdentifier(), "]Q3"); + assert_eq!(result.text(), "2001"); // "(20)01" + + // GS1 "NUM(4) 2001 FNC1(1st) 301" - FNC1(1st) can occur anywhere (this actually violates the specification) + let result = DecodeBitStream( + &[0x10, 0x10, 0xC8, 0x15, 0x10, 0x0D, 0x2D, 0x00], + version, + ecLevel, + ) + .unwrap(); + assert_eq!(result.symbologyIdentifier(), "]Q3"); + assert_eq!(result.text(), "2001301"); // "(20)01(30)1" + + // AIM "FNC1(2nd) 99 (0x63) ANUM(1) A" + let result = DecodeBitStream(&[0x96, 0x32, 0x00, 0x94, 0x00], version, ecLevel).unwrap(); + assert_eq!(result.symbologyIdentifier(), "]Q5"); + assert_eq!(result.text(), "99A"); + + // AIM "BYTE(1) A FNC1(2nd) 99 (0x63) BYTE(1) B" - FNC1(2nd) can occur anywhere + // Disabled this test, since this violates the specification and the code does support it anymore + // result = DecodeBitStream({0x40, 0x14, 0x19, 0x63, 0x40, 0x14, 0x20, 0x00}, version, ecLevel, ""); + // assert_eq!(result.symbologyIdentifier(), "]Q5"); + // assert_eq!(result.text(), L"99AB"); // Application Indicator prefixed to data + + // AIM "FNC1(2nd) A (100 + 61 = 0xA5) ANUM(1) B" + let result = DecodeBitStream(&[0x9A, 0x52, 0x00, 0x96, 0x00], version, ecLevel).unwrap(); + assert_eq!(result.symbologyIdentifier(), "]Q5"); + assert_eq!(result.text(), "AB"); + + // AIM "FNC1(2nd) a (100 + 97 = 0xC5) ANUM(1) B" + let result = DecodeBitStream(&[0x9C, 0x52, 0x00, 0x96, 0x00], version, ecLevel).unwrap(); + assert_eq!(result.symbologyIdentifier(), "]Q5"); + assert_eq!(result.text(), "aB"); + + // Bad AIM Application Indicator "FNC1(2nd) @ (0xA4) ANUM(1) B" + let result = DecodeBitStream(&[0x9A, 0x42, 0x00, 0x96, 0x00], version, ecLevel).unwrap(); + assert!(!result.isValid()); +} diff --git a/src/qrcode/cpp_port/test/QREncoderTest.cpp b/src/qrcode/cpp_port/test/QREncoderTest.cpp new file mode 100644 index 0000000..8e28abc --- /dev/null +++ b/src/qrcode/cpp_port/test/QREncoderTest.cpp @@ -0,0 +1,657 @@ +/* + * Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "BitArray.h" +#include "BitArrayUtility.h" +#include "BitMatrixIO.h" +#include "CharacterSet.h" +#include "TextDecoder.h" +#include "Utf.h" +#include "qrcode/QREncoder.h" +#include "qrcode/QRCodecMode.h" +#include "qrcode/QREncodeResult.h" +#include "qrcode/QRErrorCorrectionLevel.h" + +#include "gtest/gtest.h" + +namespace ZXing { + namespace QRCode { + int GetAlphanumericCode(int code); + CodecMode ChooseMode(const std::wstring& content, CharacterSet encoding); + void AppendModeInfo(CodecMode mode, BitArray& bits); + void AppendLengthInfo(int numLetters, const Version& version, CodecMode mode, BitArray& bits); + void AppendNumericBytes(const std::wstring& content, BitArray& bits); + void AppendAlphanumericBytes(const std::wstring& content, BitArray& bits); + void Append8BitBytes(const std::wstring& content, CharacterSet encoding, BitArray& bits); + void AppendKanjiBytes(const std::wstring& content, BitArray& bits); + void AppendBytes(const std::wstring& content, CodecMode mode, CharacterSet encoding, BitArray& bits); + void TerminateBits(int numDataBytes, BitArray& bits); + void GetNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes, int numRSBlocks, int blockID, int& numDataBytesInBlock, int& numECBytesInBlock); + void GenerateECBytes(const ByteArray& dataBytes, int numEcBytesInBlock, ByteArray& ecBytes); + BitArray InterleaveWithECBytes(const BitArray& bits, int numTotalBytes, int numDataBytes, int numRSBlocks); + } +} + +using namespace ZXing; +using namespace ZXing::QRCode; +using namespace ZXing::Utility; + +namespace { + std::wstring ShiftJISString(const std::vector& bytes) + { + std::string str; + TextDecoder::Append(str, bytes.data(), bytes.size(), CharacterSet::Shift_JIS); + return FromUtf8(str); + } + + std::string RemoveSpace(std::string s) + { + s.erase(std::remove(s.begin(), s.end(), ' '), s.end()); + return s; + } +} + +TEST(QREncoderTest, GetAlphanumericCode) +{ + // The first ten code points are numbers. + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(i, GetAlphanumericCode('0' + i)); + } + + // The next 26 code points are capital alphabet letters. + for (int i = 10; i < 36; ++i) { + EXPECT_EQ(i, GetAlphanumericCode('A' + i - 10)); + } + + // Others are symbol letters + EXPECT_EQ(36, GetAlphanumericCode(' ')); + EXPECT_EQ(37, GetAlphanumericCode('$')); + EXPECT_EQ(38, GetAlphanumericCode('%')); + EXPECT_EQ(39, GetAlphanumericCode('*')); + EXPECT_EQ(40, GetAlphanumericCode('+')); + EXPECT_EQ(41, GetAlphanumericCode('-')); + EXPECT_EQ(42, GetAlphanumericCode('.')); + EXPECT_EQ(43, GetAlphanumericCode('/')); + EXPECT_EQ(44, GetAlphanumericCode(':')); + + // Should return -1 for other letters; + EXPECT_EQ(-1, GetAlphanumericCode('a')); + EXPECT_EQ(-1, GetAlphanumericCode('#')); + EXPECT_EQ(-1, GetAlphanumericCode('\0')); +} + +TEST(QREncoderTest, ChooseMode) +{ + // Numeric mode. + EXPECT_EQ(CodecMode::NUMERIC, ChooseMode(L"0", CharacterSet::Unknown)); + EXPECT_EQ(CodecMode::NUMERIC, ChooseMode(L"0123456789", CharacterSet::Unknown)); + // Alphanumeric mode. + EXPECT_EQ(CodecMode::ALPHANUMERIC, ChooseMode(L"A", CharacterSet::Unknown)); + EXPECT_EQ(CodecMode::ALPHANUMERIC, + ChooseMode(L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", CharacterSet::Unknown)); + // 8-bit byte mode. + EXPECT_EQ(CodecMode::BYTE, ChooseMode(L"a", CharacterSet::Unknown)); + EXPECT_EQ(CodecMode::BYTE, ChooseMode(L"#", CharacterSet::Unknown)); + EXPECT_EQ(CodecMode::BYTE, ChooseMode(L"", CharacterSet::Unknown)); + // Kanji mode. We used to use MODE_KANJI for these, but we stopped + // doing that as we cannot distinguish Shift_JIS from other encodings + // from data bytes alone. See also comments in qrcode_encoder.h. + + // AIUE in Hiragana in Shift_JIS + EXPECT_EQ(CodecMode::BYTE, + ChooseMode(ShiftJISString({0x8, 0xa, 0x8, 0xa, 0x8, 0xa, 0x8, 0xa6}), CharacterSet::Unknown)); + + // Nihon in Kanji in Shift_JIS. + EXPECT_EQ(CodecMode::BYTE, ChooseMode(ShiftJISString({0x9, 0xf, 0x9, 0x7b}), CharacterSet::Unknown)); + + // Sou-Utsu-Byou in Kanji in Shift_JIS. + EXPECT_EQ(CodecMode::BYTE, ChooseMode(ShiftJISString({0xe, 0x4, 0x9, 0x5, 0x9, 0x61}), CharacterSet::Unknown)); +} + +TEST(QREncoderTest, Encode) +{ + auto qrCode = Encode(L"ABCDEF", ErrorCorrectionLevel::High, CharacterSet::Unknown, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::ALPHANUMERIC); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 4); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X \n" + " X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X \n" + " X X X X X X X X X X X X X \n" + " X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeWithVersion) +{ + auto qrCode = Encode(L"ABCDEF", ErrorCorrectionLevel::High, CharacterSet::Unknown, 7, false, -1); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 7); +} + +TEST(QREncoderTest, EncodeWithVersionTooSmall) +{ + EXPECT_THROW( + Encode(L"THISMESSAGEISTOOLONGFORAQRCODEVERSION3", ErrorCorrectionLevel::High, CharacterSet::Unknown, 3, false, -1) + , std::invalid_argument); +} + +TEST(QREncoderTest, SimpleUTF8ECI) +{ + auto qrCode = Encode(L"hello", ErrorCorrectionLevel::High, CharacterSet::UTF8, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::BYTE); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 6); + EXPECT_EQ(ToString(qrCode.matrix), // break the line comment + "X X X X X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X X \n" + " X X X X X X X \n" + " X X X X X X X X X \n" + "X X X X X X X X X X X \n" + " X X X X X X \n" + " X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, SimpleBINARYECI) +{ + auto qrCode = Encode(L"\u00E9", ErrorCorrectionLevel::High, CharacterSet::BINARY, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::BYTE); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 6); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X X X \n" + "X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X \n" + " X X X X X X X \n" + "X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + " X X X X X X X X X X \n" + " X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeKanjiMode) +{ + auto qrCode = Encode(L"\u65e5\u672c", ErrorCorrectionLevel::Medium, CharacterSet::Shift_JIS, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::KANJI); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::Medium); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 0); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X \n" + "X X X X X X X X \n" + "X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + " X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeShiftjisNumeric) +{ + auto qrCode = Encode(L"0123", ErrorCorrectionLevel::Medium, CharacterSet::Shift_JIS, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::NUMERIC); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::Medium); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 2); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X \n" + " X X X X X X X X X X X X X X \n" + "X X X X X X X X X \n" + " X X X X X X X X \n" + " X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeGS1) +{ + auto qrCode = Encode(L"100001%11171218", ErrorCorrectionLevel::High, CharacterSet::Unknown, 0, true, -1); + EXPECT_EQ(qrCode.mode, CodecMode::ALPHANUMERIC); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 2); + EXPECT_EQ(qrCode.maskPattern, 4); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X X X X X \n" + "X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X \n" + " X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + " X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeGS1ModeHeaderWithECI) +{ + auto qrCode = Encode(L"hello", ErrorCorrectionLevel::High, CharacterSet::UTF8, 0, true, -1); + EXPECT_EQ(qrCode.mode, CodecMode::BYTE); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 5); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X X \n" + " X X X X X X X X \n" + " X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + " X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, AppendModeInfo) +{ + BitArray bits; + AppendModeInfo(CodecMode::NUMERIC, bits); + EXPECT_EQ(ToString(bits), "...X"); +} + +TEST(QREncoderTest, AppendLengthInfo) +{ + BitArray bits; + AppendLengthInfo(1, // 1 letter (1/1). + *Version::FromNumber(1), CodecMode::NUMERIC, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("........ .X")); // 10 bits. + + bits = BitArray(); + AppendLengthInfo(2, // 2 letters (2/1). + *Version::FromNumber(10), CodecMode::ALPHANUMERIC, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("........ .X.")); // 11 bits. + + bits = BitArray(); + AppendLengthInfo(255, // 255 letter (255/1). + *Version::FromNumber(27), CodecMode::BYTE, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("........ XXXXXXXX")); // 16 bits. + + bits = BitArray(); + AppendLengthInfo(512, // 512 letters (1024/2). + *Version::FromNumber(40), CodecMode::KANJI, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..X..... ....")); // 12 bits. +} + +TEST(QREncoderTest, AppendNumericBytes) +{ + // 1 = 01 = 0001 in 4 bits. + BitArray bits; + AppendNumericBytes(L"1", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...X")); + + // 12 = 0xc = 0001100 in 7 bits. + bits = BitArray(); + AppendNumericBytes(L"12", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...XX..")); + + // 123 = 0x7b = 0001111011 in 10 bits. + bits = BitArray(); + AppendNumericBytes(L"123", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...XXXX. XX")); + + // 1234 = "123" + "4" = 0001111011 + 0100 + bits = BitArray(); + AppendNumericBytes(L"1234", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...XXXX. XX.X..")); + + // Empty. + bits = BitArray(); + AppendNumericBytes(L"", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("")); +} + +TEST(QREncoderTest, AppendAlphanumericBytes) +{ + // A = 10 = 0xa = 001010 in 6 bits + BitArray bits; + AppendAlphanumericBytes(L"A", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..X.X.")); + + // AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits + bits = BitArray(); + AppendAlphanumericBytes(L"AB", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..XXX..X X.X")); + + // ABC = "AB" + "C" = 00111001101 + 001100 + bits = BitArray(); + AppendAlphanumericBytes(L"ABC", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..XXX..X X.X..XX. .")); + + // Empty. + bits = BitArray(); + AppendAlphanumericBytes(L"", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("")); + + // Invalid data. + EXPECT_THROW(AppendAlphanumericBytes(L"abc", bits), std::invalid_argument); +} + +TEST(QREncoderTest, Append8BitBytes) +{ + // 0x61, 0x62, 0x63 + BitArray bits; + Append8BitBytes(L"abc", CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX....X .XX...X. .XX...XX")); + + // Empty. + bits = BitArray(); + Append8BitBytes(L"", CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("")); +} + +// Numbers are from page 21 of JISX0510:2004 +TEST(QREncoderTest, AppendKanjiBytes) +{ + BitArray bits; + AppendKanjiBytes(ShiftJISString({ 0x93, 0x5f }), bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX.XX.. XXXXX")); + + AppendKanjiBytes(ShiftJISString({ 0xe4, 0xaa }), bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX.XX.. XXXXXXX. X.X.X.X. X.")); +} + +TEST(QREncoderTest, AppendBytes) +{ + // Should use appendNumericBytes. + // 1 = 01 = 0001 in 4 bits. + BitArray bits; + AppendBytes(L"1", CodecMode::NUMERIC, CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...X")); + + // Should use appendAlphanumericBytes. + // A = 10 = 0xa = 001010 in 6 bits + bits = BitArray(); + AppendBytes(L"A", CodecMode::ALPHANUMERIC, CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..X.X.")); + + // Lower letters such as 'a' cannot be encoded in MODE_ALPHANUMERIC. + bits = BitArray(); + EXPECT_THROW(AppendBytes(L"a", CodecMode::ALPHANUMERIC, CharacterSet::Unknown, bits), std::invalid_argument); + + // Should use append8BitBytes. + // 0x61, 0x62, 0x63 + bits = BitArray(); + AppendBytes(L"abc", CodecMode::BYTE, CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX....X .XX...X. .XX...XX")); + + // Anything can be encoded in QRCode.MODE_8BIT_BYTE. + AppendBytes(L"\0", CodecMode::BYTE, CharacterSet::Unknown, bits); + + // Should use appendKanjiBytes. + // 0x93, 0x5f + bits = BitArray(); + AppendBytes(ShiftJISString({0x93, 0x5f}), CodecMode::KANJI, CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX.XX.. XXXXX")); +} + +TEST(QREncoderTest, TerminateBits) +{ + BitArray v; + TerminateBits(0, v); + EXPECT_EQ(ToString(v), RemoveSpace("")); + + v = BitArray(); + TerminateBits(1, v); + EXPECT_EQ(ToString(v), RemoveSpace("........")); + + v = BitArray(); + v.appendBits(0, 3); + TerminateBits(1, v); + EXPECT_EQ(ToString(v), RemoveSpace("........")); + + v = BitArray(); + v.appendBits(0, 5); + TerminateBits(1, v); + EXPECT_EQ(ToString(v), RemoveSpace("........")); + + v = BitArray(); + v.appendBits(0, 8); + TerminateBits(1, v); + EXPECT_EQ(ToString(v), RemoveSpace("........")); + + v = BitArray(); + TerminateBits(2, v); + EXPECT_EQ(ToString(v), RemoveSpace("........ XXX.XX..")); + + v = BitArray(); + v.appendBits(0, 1); + TerminateBits(3, v); + EXPECT_EQ(ToString(v), RemoveSpace("........ XXX.XX.. ...X...X")); +} + +TEST(QREncoderTest, GetNumDataBytesAndNumECBytesForBlockID) +{ + int numDataBytes; + int numEcBytes; + // Version 1-H. + GetNumDataBytesAndNumECBytesForBlockID(26, 9, 1, 0, numDataBytes, numEcBytes); + EXPECT_EQ(9, numDataBytes); + EXPECT_EQ(17, numEcBytes); + + // Version 3-H. 2 blocks. + GetNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 0, numDataBytes, numEcBytes); + EXPECT_EQ(13, numDataBytes); + EXPECT_EQ(22, numEcBytes); + GetNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 1, numDataBytes, numEcBytes); + EXPECT_EQ(13, numDataBytes); + EXPECT_EQ(22, numEcBytes); + + // Version 7-H. (4 + 1) blocks. + GetNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 0, numDataBytes, numEcBytes); + EXPECT_EQ(13, numDataBytes); + EXPECT_EQ(26, numEcBytes); + GetNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 4, numDataBytes, numEcBytes); + EXPECT_EQ(14, numDataBytes); + EXPECT_EQ(26, numEcBytes); + + // Version 40-H. (20 + 61) blocks. + GetNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 0, numDataBytes, numEcBytes); + EXPECT_EQ(15, numDataBytes); + EXPECT_EQ(30, numEcBytes); + GetNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 20, numDataBytes, numEcBytes); + EXPECT_EQ(16, numDataBytes); + EXPECT_EQ(30, numEcBytes); + GetNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 80, numDataBytes, numEcBytes); + EXPECT_EQ(16, numDataBytes); + EXPECT_EQ(30, numEcBytes); +} + +// Numbers are from http://www.swetake.com/qr/qr3.html and +// http://www.swetake.com/qr/qr9.html +TEST(QREncoderTest, GenerateECBytes) +{ + ByteArray ecBytes; + GenerateECBytes({ 32, 65, 205, 69, 41, 220, 46, 128, 236 }, 17, ecBytes); + ByteArray expected = { 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61 }; + EXPECT_EQ(ecBytes, expected); + + GenerateECBytes({ 67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214 }, 18, ecBytes); + expected = { 175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187 }; + EXPECT_EQ(ecBytes, expected); + + // High-order zero coefficient case. + GenerateECBytes({ 32, 49, 205, 69, 42, 20, 0, 236, 17 }, 17, ecBytes); + expected = { 0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213 }; + EXPECT_EQ(ecBytes, expected); +} + +TEST(QREncoderTest, InterleaveWithECBytes) +{ + BitArray in; + for (int dataByte : {32, 65, 205, 69, 41, 220, 46, 128, 236}) + in.appendBits(dataByte, 8); + + BitArray out = InterleaveWithECBytes(in, 26, 9, 1); + std::vector expected = { + 32, 65, 205, 69, 41, 220, 46, 128, 236, + + 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61, + }; // Error correction bytes in second block + ASSERT_EQ(Size(expected), out.sizeInBytes()); + EXPECT_EQ(expected, out.toBytes()); + + // Numbers are from http://www.swetake.com/qr/qr8.html + in = BitArray(); + for (int dataByte : + {67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214, 230, 247, 7, 23, 39, 55, + 71, 87, 103, 119, 135, 151, 166, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214, 230, + 247, 7, 23, 39, 55, 71, 87, 103, 119, 135, 151, 160, 236, 17, 236, 17, 236, 17, 236, 17}) + in.appendBits(dataByte, 8); + + out = InterleaveWithECBytes(in, 134, 62, 4); + expected = { + 67, 230, 54, 55, 70, 247, 70, 71, 22, 7, 86, 87, 38, 23, 102, 103, 54, 39, 118, 119, 70, + 55, 134, 135, 86, 71, 150, 151, 102, 87, 166, 160, 118, 103, 182, 236, 134, 119, 198, 17, 150, 135, + 214, 236, 166, 151, 230, 17, 182, 166, 247, 236, 198, 22, 7, 17, 214, 38, 23, 236, 39, 17, + + 175, 155, 245, 236, 80, 146, 56, 74, 155, 165, 133, 142, 64, 183, 132, 13, 178, 54, 132, 108, 45, + 113, 53, 50, 214, 98, 193, 152, 233, 147, 50, 71, 65, 190, 82, 51, 209, 199, 171, 54, 12, 112, + 57, 113, 155, 117, 211, 164, 117, 30, 158, 225, 31, 190, 242, 38, 140, 61, 179, 154, 214, 138, 147, + 87, 27, 96, 77, 47, 187, 49, 156, 214, + }; // Error correction bytes in second block + EXPECT_EQ(Size(expected), out.sizeInBytes()); + EXPECT_EQ(expected, out.toBytes()); +} + +TEST(QREncoderTest, BugInBitVectorNumBytes) +{ + // There was a bug in BitVector.sizeInBytes() that caused it to return a + // smaller-by-one value (ex. 1465 instead of 1466) if the number of bits + // in the vector is not 8-bit aligned. In QRCodeEncoder::InitQRCode(), + // BitVector::sizeInBytes() is used for finding the smallest QR Code + // version that can fit the given data. Hence there were corner cases + // where we chose a wrong QR Code version that cannot fit the given + // data. Note that the issue did not occur with MODE_8BIT_BYTE, as the + // bits in the bit vector are always 8-bit aligned. + // + // Before the bug was fixed, the following test didn't pass, because: + // + // - MODE_NUMERIC is chosen as all bytes in the data are '0' + // - The 3518-byte numeric data needs 1466 bytes + // - 3518 / 3 * 10 + 7 = 11727 bits = 1465.875 bytes + // - 3 numeric bytes are encoded in 10 bits, hence the first + // 3516 bytes are encoded in 3516 / 3 * 10 = 11720 bits. + // - 2 numeric bytes can be encoded in 7 bits, hence the last + // 2 bytes are encoded in 7 bits. + // - The version 27 QR Code with the EC level L has 1468 bytes for data. + // - 1828 - 360 = 1468 + // - In InitQRCode(), 3 bytes are reserved for a header. Hence 1465 bytes + // (1468 -3) are left for data. + // - Because of the bug in BitVector::sizeInBytes(), InitQRCode() determines + // the given data can fit in 1465 bytes, despite it needs 1466 bytes. + // - Hence QRCodeEncoder.encode() failed and returned false. + // - To be precise, it needs 11727 + 4 (getMode info) + 14 (length info) = + // 11745 bits = 1468.125 bytes are needed (i.e. cannot fit in 1468 + // bytes). + Encode(std::wstring(3518, L'0'), ErrorCorrectionLevel::Low, CharacterSet::Unknown, 0, false, -1); +} diff --git a/src/qrcode/cpp_port/test/QRErrorCorrectionLevelTest.rs b/src/qrcode/cpp_port/test/QRErrorCorrectionLevelTest.rs new file mode 100644 index 0000000..a8eb509 --- /dev/null +++ b/src/qrcode/cpp_port/test/QRErrorCorrectionLevelTest.rs @@ -0,0 +1,89 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +// #include "qrcode/QRErrorCorrectionLevel.h" + +// #include "gtest/gtest.h" + +// using namespace ZXing; +// using namespace ZXing::QRCode; + +use crate::qrcode::decoder::ErrorCorrectionLevel; + +#[test] +fn ForBits() { + assert_eq!( + ErrorCorrectionLevel::M, + ErrorCorrectionLevel::ECLevelFromBits(0, false) + ); + assert_eq!( + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::ECLevelFromBits(1, false) + ); + assert_eq!( + ErrorCorrectionLevel::H, + ErrorCorrectionLevel::ECLevelFromBits(2, false) + ); + assert_eq!( + ErrorCorrectionLevel::Q, + ErrorCorrectionLevel::ECLevelFromBits(3, false) + ); +} + +#[test] +fn ForMicroBits() { + assert_eq!( + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::ECLevelFromBits(0, true) + ); + assert_eq!( + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::ECLevelFromBits(1, true) + ); + assert_eq!( + ErrorCorrectionLevel::M, + ErrorCorrectionLevel::ECLevelFromBits(2, true) + ); + assert_eq!( + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::ECLevelFromBits(3, true) + ); + assert_eq!( + ErrorCorrectionLevel::M, + ErrorCorrectionLevel::ECLevelFromBits(4, true) + ); + assert_eq!( + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::ECLevelFromBits(5, true) + ); + assert_eq!( + ErrorCorrectionLevel::M, + ErrorCorrectionLevel::ECLevelFromBits(6, true) + ); + assert_eq!( + ErrorCorrectionLevel::Q, + ErrorCorrectionLevel::ECLevelFromBits(7, true) + ); + + assert_eq!( + ErrorCorrectionLevel::Q, + ErrorCorrectionLevel::ECLevelFromBitsSigned(-1, true) + ); + assert_eq!( + ErrorCorrectionLevel::L, + ErrorCorrectionLevel::ECLevelFromBits(8, true) + ); +} + +#[test] +fn ToString() { + // using namespace std::literals; + + assert_eq!("L", ErrorCorrectionLevel::L.to_string()); + assert_eq!("M", ErrorCorrectionLevel::M.to_string()); + assert_eq!("Q", ErrorCorrectionLevel::Q.to_string()); + assert_eq!("H", ErrorCorrectionLevel::H.to_string()); +} diff --git a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs new file mode 100644 index 0000000..67f326a --- /dev/null +++ b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs @@ -0,0 +1,132 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2007 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::qrcode::decoder::{ErrorCorrectionLevel, FormatInformation}; + +const MASKED_TEST_FORMAT_INFO: u32 = 0x2BED; +const MASKED_TEST_FORMAT_INFO2: u32 = + ((0x2BED << 1) & 0b1111111000000000) | 0b100000000 | (0x2BED & 0b11111111); // insert the 'Dark Module' +const UNMASKED_TEST_FORMAT_INFO: u32 = MASKED_TEST_FORMAT_INFO ^ 0x5412; +const MICRO_MASKED_TEST_FORMAT_INFO: u32 = 0x3BBA; +// const MICRO_UNMASKED_TEST_FORMAT_INFO: u32 = MICRO_MASKED_TEST_FORMAT_INFO ^ 0x4445; + +fn DoFormatInformationTest(formatInfo: u32, expectedMask: u8, expectedECL: ErrorCorrectionLevel) { + let parsedFormat = FormatInformation::DecodeMQR(formatInfo); + assert!(parsedFormat.isValid()); + assert_eq!(expectedMask, parsedFormat.data_mask); + assert_eq!(expectedECL, parsedFormat.error_correction_level); +} + +#[test] +fn Decode() { + // Normal case + let expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2); + assert!(expected.isValid()); + assert_eq!(0x07, expected.data_mask); + assert_eq!(ErrorCorrectionLevel::Q, expected.error_correction_level); + // where the code forgot the mask! + // assert_eq!( + // expected, + // FormatInformation::DecodeQR(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2) + // ); + cpp_eq( + &expected, + &FormatInformation::DecodeQR(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2), + ) +} + +#[test] +fn DecodeWithBitDifference() { + let expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2); + // 1,2,3,4 bits difference + cpp_eq( + &expected, + &FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x01, + MASKED_TEST_FORMAT_INFO2 ^ 0x01, + ), + ); + cpp_eq( + &expected, + &FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x03, + MASKED_TEST_FORMAT_INFO2 ^ 0x03, + ), + ); + cpp_eq( + &expected, + &FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x07, + MASKED_TEST_FORMAT_INFO2 ^ 0x07, + ), + ); + assert!(!FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x0F, + MASKED_TEST_FORMAT_INFO2 ^ 0x0F + ) + .isValid()); +} + +#[test] +fn DecodeWithMisread() { + let expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2); + cpp_eq( + &expected, + &FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x03, + MASKED_TEST_FORMAT_INFO2 ^ 0x0F, + ), + ); +} + +#[test] +fn DecodeMicro() { + // Normal cases. + DoFormatInformationTest(0x4445, 0x0, ErrorCorrectionLevel::L); + DoFormatInformationTest(0x4172, 0x1, ErrorCorrectionLevel::L); + DoFormatInformationTest(0x5fc0, 0x2, ErrorCorrectionLevel::L); + DoFormatInformationTest(0x5af7, 0x3, ErrorCorrectionLevel::L); + DoFormatInformationTest(0x6793, 0x0, ErrorCorrectionLevel::M); + DoFormatInformationTest(0x62a4, 0x1, ErrorCorrectionLevel::M); + DoFormatInformationTest(0x3e8d, 0x2, ErrorCorrectionLevel::Q); + DoFormatInformationTest(MICRO_MASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Q); + + // where the code forgot the mask! + // DoFormatInformationTest(MICRO_UNMASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Quality); +} + +// This doesn't work as expected because the implementation of the decode tries with +// and without the mask (0x4445). This effectively adds a tolerance of 5 bits to the Hamming +// distance calculation. +#[test] +fn DecodeMicroWithBitDifference() { + let expected = FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO); + + // 1,2,3 bits difference + cpp_eq( + &expected, + &FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x01), + ); + cpp_eq( + &expected, + &FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x03), + ); + cpp_eq( + &expected, + &FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x07), + ); + + // Bigger bit differences can return valid FormatInformation objects but the data mask and error + // correction levels do not match. + // EXPECT_TRUE(FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).isValid()); + // EXPECT_NE(expected.dataMask(), FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).dataMask()); + // EXPECT_NE(expected.errorCorrectionLevel(), + // FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).errorCorrectionLevel()); +} + +fn cpp_eq(rhs: &FormatInformation, lhs: &FormatInformation) { + assert!(rhs.cpp_eq(lhs)) +} diff --git a/src/qrcode/cpp_port/test/QRModeTest.rs b/src/qrcode/cpp_port/test/QRModeTest.rs new file mode 100644 index 0000000..fa356a0 --- /dev/null +++ b/src/qrcode/cpp_port/test/QRModeTest.rs @@ -0,0 +1,135 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::qrcode::decoder::{Mode, Version}; + +#[test] +fn ForBits() { + assert_eq!( + Mode::TERMINATOR, + Mode::CodecModeForBits(0x00, None).unwrap() + ); + assert_eq!(Mode::NUMERIC, Mode::CodecModeForBits(0x01, None).unwrap()); + assert_eq!( + Mode::ALPHANUMERIC, + Mode::CodecModeForBits(0x02, None).unwrap() + ); + assert_eq!(Mode::BYTE, Mode::CodecModeForBits(0x04, None).unwrap()); + assert_eq!(Mode::KANJI, Mode::CodecModeForBits(0x08, None).unwrap()); + assert!(Mode::CodecModeForBits(0x10, None).is_err()); +} + +#[test] +fn CharacterCount() { + // Spot check a few values + assert_eq!( + 10, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(5, false).unwrap()) + ); + assert_eq!( + 12, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(26, false).unwrap()) + ); + assert_eq!( + 14, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(40, false).unwrap()) + ); + assert_eq!( + 9, + Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(6, false).unwrap()) + ); + assert_eq!( + 8, + Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false).unwrap()) + ); + assert_eq!( + 8, + Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false).unwrap()) + ); +} + +#[test] +fn MicroForBits() { + // M1 + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x00, Some(true)).unwrap() + ); + // M2 + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x00, Some(true)).unwrap() + ); + assert_eq!( + Mode::ALPHANUMERIC, + Mode::CodecModeForBits(0x01, Some(true)).unwrap() + ); + // M3 + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x00, Some(true)).unwrap() + ); + assert_eq!( + Mode::ALPHANUMERIC, + Mode::CodecModeForBits(0x01, Some(true)).unwrap() + ); + assert_eq!( + Mode::BYTE, + Mode::CodecModeForBits(0x02, Some(true)).unwrap() + ); + assert_eq!( + Mode::KANJI, + Mode::CodecModeForBits(0x03, Some(true)).unwrap() + ); + // M4 + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x00, Some(true)).unwrap() + ); + assert_eq!( + Mode::ALPHANUMERIC, + Mode::CodecModeForBits(0x01, Some(true)).unwrap() + ); + assert_eq!( + Mode::BYTE, + Mode::CodecModeForBits(0x02, Some(true)).unwrap() + ); + assert_eq!( + Mode::KANJI, + Mode::CodecModeForBits(0x03, Some(true)).unwrap() + ); + + assert!(Mode::CodecModeForBits(0x04, Some(true)).is_err()); +} + +#[test] +fn MicroCharacterCount() { + // Spot check a few values + assert_eq!( + 3, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true).unwrap()) + ); + assert_eq!( + 4, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true).unwrap()) + ); + assert_eq!( + 6, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true).unwrap()) + ); + assert_eq!( + 3, + Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(2, true).unwrap()) + ); + assert_eq!( + 4, + Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true).unwrap()) + ); + assert_eq!( + 4, + Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true).unwrap()) + ); +} diff --git a/src/qrcode/cpp_port/test/QRVersionTest.rs b/src/qrcode/cpp_port/test/QRVersionTest.rs new file mode 100644 index 0000000..0f79ecf --- /dev/null +++ b/src/qrcode/cpp_port/test/QRVersionTest.rs @@ -0,0 +1,122 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::BitMatrix, + qrcode::decoder::{Version, VersionRef}, +}; + +fn CheckVersion(version: VersionRef, number: u32, dimension: u32) { + // assert_ne!(version, nullptr); + assert_eq!(number, version.getVersionNumber()); + if (number > 1 && !version.isMicroQRCode()) { + assert!(!version.getAlignmentPatternCenters().is_empty()); + } + assert_eq!(dimension, version.getDimensionForVersion()); +} + +fn DoTestVersion(expectedVersion: u32, mask: i32) { + let version = Version::DecodeVersionInformation(mask, 0).expect("should exist"); + // assert_ne!(version, nullptr); + assert_eq!(expectedVersion, version.getVersionNumber()); +} + +#[test] +fn VersionForNumber() { + let version = Version::FromNumber(0, false); + assert!(version.is_err(), "There is version with number 0"); + + for i in 1..=40 { + // for (int i = 1; i <= 40; i++) { + CheckVersion( + Version::FromNumber(i, false).expect("version number found"), + i, + 4 * i + 17, + ); + } +} + +#[test] +fn GetProvisionalVersionForDimension() { + for i in 1..=40 { + // for (int i = 1; i <= 40; i++) { + let prov = + Version::FromDimension(4 * i + 17).expect(&format!("version should exist for {i}")); + // assert_ne!(prov, nullptr); + assert_eq!(i, prov.getVersionNumber()); + } +} + +#[test] +fn DecodeVersionInformation() { + // Spot check + DoTestVersion(7, 0x07C94); + DoTestVersion(12, 0x0C762); + DoTestVersion(17, 0x1145D); + DoTestVersion(22, 0x168C9); + DoTestVersion(27, 0x1B08E); + DoTestVersion(32, 0x209D5); +} + +#[test] +fn MicroVersionForNumber() { + let version = Version::FromNumber(0, true); + assert!(version.is_err(), "There is version with number 0"); + + for i in 1..=4 { + // for (int i = 1; i <= 4; i++) { + CheckVersion( + Version::FromNumber(i, true).expect(&format!("version for {i} should exist")), + i, + 2 * i + 9, + ); + } +} + +#[test] +fn GetProvisionalMicroVersionForDimension() { + for i in 1..=4 { + // for (int i = 1; i <= 4; i++) { + let prov = Version::FromDimension(2 * i + 9) + .expect(&format!("version for micro {i} should exist")); + // assert_ne!(prov, nullptr); + assert_eq!(i, prov.getVersionNumber()); + } +} + +#[test] +fn FunctionPattern() { + let testFinderPatternRegion = |bitMatrix: &BitMatrix| { + for row in 0..9 { + // for (int row = 0; row < 9; row++){ + for col in 0..9 { + // for (int col = 0; col < 9; col++) { + assert!(bitMatrix.get(col, row)); + } + } + }; + for i in 1..=4 { + // for (int i = 1; i <= 4; i++) { + let version = Version::FromNumber(i, true).expect("version must be found"); + let functionPattern = version + .buildFunctionPattern() + .expect("function pattern must be found"); + testFinderPatternRegion(&functionPattern); + + // Check timing pattern areas. + let dimension = version.getDimensionForVersion(); + for row in dimension..functionPattern.height() + // for (int row = dimension; row < functionPattern.height(); row++) + { + assert!(functionPattern.get(0, row)); + } + for col in dimension..functionPattern.width() + // for (int col = dimension; col < functionPattern.width(); col++) + { + assert!(functionPattern.get(col, 0)); + } + } +} diff --git a/src/qrcode/cpp_port/test/mod.rs b/src/qrcode/cpp_port/test/mod.rs new file mode 100644 index 0000000..4dd3017 --- /dev/null +++ b/src/qrcode/cpp_port/test/mod.rs @@ -0,0 +1,14 @@ +mod QRModeTest; +mod QRVersionTest; + +mod QRFormatInformationTest; + +mod QRErrorCorrectionLevelTest; + +mod QRDecodedBitStreamParserTest; + +mod QRDataMaskTest; + +mod QRBitMatrixParserTest; + +mod MQRDecoderTest; diff --git a/src/qrcode/decoder/error_correction_level.rs b/src/qrcode/decoder/error_correction_level.rs index 9b61495..b3be3bc 100644 --- a/src/qrcode/decoder/error_correction_level.rs +++ b/src/qrcode/decoder/error_correction_level.rs @@ -14,6 +14,7 @@ * limitations under the License. */ +use std::fmt::{self, Display}; use std::str::FromStr; use crate::common::Result; @@ -35,6 +36,7 @@ pub enum ErrorCorrectionLevel { Q, //0x03 /** H = ~30% correction */ H, //0x02 + Invalid, } impl ErrorCorrectionLevel { @@ -60,6 +62,7 @@ impl ErrorCorrectionLevel { ErrorCorrectionLevel::M => 0x00, ErrorCorrectionLevel::Q => 0x03, ErrorCorrectionLevel::H => 0x02, + ErrorCorrectionLevel::Invalid => 0x00, } } @@ -69,6 +72,7 @@ impl ErrorCorrectionLevel { ErrorCorrectionLevel::M => 1, ErrorCorrectionLevel::Q => 2, ErrorCorrectionLevel::H => 3, + ErrorCorrectionLevel::Invalid => 100, } } } @@ -115,3 +119,15 @@ impl FromStr for ErrorCorrectionLevel { ))); } } + +impl Display for ErrorCorrectionLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + ErrorCorrectionLevel::L => "L", + ErrorCorrectionLevel::M => "M", + ErrorCorrectionLevel::Q => "Q", + ErrorCorrectionLevel::H => "H", + ErrorCorrectionLevel::Invalid => "_", + }) + } +} diff --git a/src/qrcode/decoder/format_information.rs b/src/qrcode/decoder/format_information.rs index b6564f4..492b445 100644 --- a/src/qrcode/decoder/format_information.rs +++ b/src/qrcode/decoder/format_information.rs @@ -18,12 +18,12 @@ use crate::common::Result; use super::ErrorCorrectionLevel; -const FORMAT_INFO_MASK_QR: u32 = 0x5412; +pub const FORMAT_INFO_MASK_QR: u32 = 0x5412; /** * See ISO 18004:2006, Annex C, Table C.1 */ -const FORMAT_INFO_DECODE_LOOKUP: [[u32; 2]; 32] = [ +pub const FORMAT_INFO_DECODE_LOOKUP: [[u32; 2]; 32] = [ [0x5412, 0x00], [0x5125, 0x01], [0x5E7C, 0x02], @@ -68,8 +68,28 @@ const FORMAT_INFO_DECODE_LOOKUP: [[u32; 2]; 32] = [ */ #[derive(Hash, Eq, PartialEq, Debug)] pub struct FormatInformation { - error_correction_level: ErrorCorrectionLevel, - data_mask: u8, + pub hammingDistance: u32, + pub error_correction_level: ErrorCorrectionLevel, + pub data_mask: u8, + pub microVersion: u32, + pub isMirrored: bool, + + pub index: u8, // = 255; + pub bitsIndex: u8, // = 255; +} + +impl Default for FormatInformation { + fn default() -> Self { + Self { + hammingDistance: 255, + error_correction_level: ErrorCorrectionLevel::Invalid, + data_mask: Default::default(), + microVersion: 0, + isMirrored: false, + index: 255, + bitsIndex: 255, + } + } } impl FormatInformation { @@ -79,8 +99,13 @@ impl FormatInformation { // Bottom 3 bits let dataMask = format_info & 0x07; Ok(Self { + hammingDistance: 255, + microVersion: 0, error_correction_level: errorCorrectionLevel, data_mask: dataMask, + isMirrored: false, + index: 255, + bitsIndex: 255, }) } diff --git a/src/qrcode/decoder/mode.rs b/src/qrcode/decoder/mode.rs index ab726f1..47e97fc 100644 --- a/src/qrcode/decoder/mode.rs +++ b/src/qrcode/decoder/mode.rs @@ -121,6 +121,81 @@ impl Mode { Mode::HANZI => 0x0D, } } + + pub const fn get_terminator_bit_length(version: &Version) -> u8 { + (if version.isMicroQRCode() { + version.getVersionNumber() * 2 + 1 + } else { + 4 + }) as u8 + } + pub const fn get_codec_mode_bits_length(version: &Version) -> u8 { + (if version.isMicroQRCode() { + version.getVersionNumber() - 1 + } else { + 4 + }) as u8 + } + /** + * @param bits variable number of bits encoding a QR Code data mode + * @param isMicro is this a MicroQRCode + * @return Mode encoded by these bits + * @throws FormatError if bits do not correspond to a known mode + */ + pub fn CodecModeForBits(bits: u32, isMicro: Option) -> Result { + let isMicro = isMicro.unwrap_or(false); + const BITS_2_MODE_LEN: usize = 4; + + if (!isMicro) { + if ((bits >= 0x00 && bits <= 0x05) || (bits >= 0x07 && bits <= 0x09) || bits == 0x0d) { + return Mode::try_from(bits); + } + } else { + const Bits2Mode: [Mode; BITS_2_MODE_LEN] = + [Mode::NUMERIC, Mode::ALPHANUMERIC, Mode::BYTE, Mode::KANJI]; + if ((bits as usize) < BITS_2_MODE_LEN) { + return Ok(Bits2Mode[bits as usize]); + } + } + + Err(Exceptions::format_with("Invalid codec mode")) + } + + /** + * @param version version in question + * @return number of bits used, in this QR Code symbol {@link Version}, to encode the + * count of characters that will follow encoded in this Mode + */ + pub fn CharacterCountBits(&self, version: &Version) -> u32 { + let number = version.getVersionNumber() as usize; + if (version.isMicroQRCode()) { + match (self) { + Mode::NUMERIC=> return [3, 4, 5, 6][number - 1], + Mode::ALPHANUMERIC=> return [3, 4, 5][number - 2], + Mode::BYTE=> return [4, 5][number - 3], + Mode::KANJI | //=> [[fallthrough]], + Mode::HANZI=> return [3, 4][number - 3], + _=> return 0, + } + } + + let i = if (number <= 9) { + 0 + } else if (number <= 26) { + 1 + } else { + 2 + }; + + match (self) { + Mode::NUMERIC=> return [10, 12, 14][i], + Mode::ALPHANUMERIC=> return [9, 11, 13][i], + Mode::BYTE=> return [8, 16, 16][i], + Mode::KANJI| // [[fallthrough]]; + Mode::HANZI=> return [8, 10, 12][i], + _=> return 0, + } + } } impl From for u8 { @@ -136,3 +211,11 @@ impl TryFrom for Mode { Self::forBits(value) } } + +impl TryFrom for Mode { + type Error = Exceptions; + + fn try_from(value: u32) -> Result { + Self::forBits(value as u8) + } +} diff --git a/src/qrcode/decoder/version.rs b/src/qrcode/decoder/version.rs index 8ca060b..aeb58c7 100755 --- a/src/qrcode/decoder/version.rs +++ b/src/qrcode/decoder/version.rs @@ -27,13 +27,14 @@ use once_cell::sync::Lazy; pub type VersionRef = &'static Version; -static VERSIONS: Lazy> = Lazy::new(Version::buildVersions); +pub static VERSIONS: Lazy> = Lazy::new(Version::buildVersions); +pub static MICRO_VERSIONS: Lazy> = Lazy::new(Version::build_micro_versions); /** * See ISO 18004:2006 Annex D. * Element i represents the raw version bits that specify version i + 7 */ -const VERSION_DECODE_INFO: [u32; 34] = [ +pub const VERSION_DECODE_INFO: [u32; 34] = [ 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, @@ -53,9 +54,10 @@ pub struct Version { alignmentPatternCenters: Vec, ecBlocks: Vec, totalCodewords: u32, + pub(crate) is_micro: bool, } impl Version { - fn new(versionNumber: u32, alignmentPatternCenters: Vec, ecBlocks: Vec) -> Self { + fn new(versionNumber: u32, alignmentPatternCenters: Vec, ecBlocks: [ECBlocks; 4]) -> Self { let mut total = 0; let ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); let ecbArray = ecBlocks[0].getECBlocks(); @@ -68,12 +70,32 @@ impl Version { Self { versionNumber, alignmentPatternCenters, - ecBlocks, + ecBlocks: ecBlocks.to_vec(), totalCodewords: total, + is_micro: false, } } - pub fn getVersionNumber(&self) -> u32 { + fn new_micro(versionNumber: u32, ecBlocks: Vec) -> Self { + let mut total = 0; + let ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); + let ecbArray = ecBlocks[0].getECBlocks(); + let mut i = 0; + while i < ecbArray.len() { + total += ecbArray[i].getCount() * (ecbArray[i].getDataCodewords() + ecCodewords); + i += 1; + } + + Self { + versionNumber, + alignmentPatternCenters: Vec::default(), + ecBlocks, + totalCodewords: total, + is_micro: true, + } + } + + pub const fn getVersionNumber(&self) -> u32 { self.versionNumber } @@ -86,10 +108,14 @@ impl Version { } pub fn getDimensionForVersion(&self) -> u32 { - 17 + 4 * self.versionNumber + Self::DimensionOfVersion(self.versionNumber, self.is_micro) + // 17 + 4 * self.versionNumber } pub fn getECBlocksForLevel(&self, ecLevel: ErrorCorrectionLevel) -> &ECBlocks { + if ecLevel.get_ordinal() as usize >= self.ecBlocks.len() { + return &self.ecBlocks[ecLevel.get_ordinal() as usize % self.ecBlocks.len()]; + } &self.ecBlocks[ecLevel.get_ordinal() as usize] } @@ -100,21 +126,21 @@ impl Version { * @return Version for a QR Code of that dimension * @throws FormatException if dimension is not 1 mod 4 */ - pub fn getProvisionalVersionForDimension(dimension: u32) -> Result<&'static Version> { + pub fn getProvisionalVersionForDimension(dimension: u32) -> Result { if dimension % 4 != 1 { return Err(Exceptions::format_with("dimension incorrect")); } Self::getVersionForNumber((dimension - 17) / 4) } - pub fn getVersionForNumber(versionNumber: u32) -> Result<&'static Version> { + pub fn getVersionForNumber(versionNumber: u32) -> Result { if !(1..=40).contains(&versionNumber) { return Err(Exceptions::illegal_argument_with("version out of spec")); } Ok(&VERSIONS[versionNumber as usize - 1]) } - pub fn decodeVersionInformation(versionBits: u32) -> Result<&'static Version> { + pub fn decodeVersionInformation(versionBits: u32) -> Result { let mut bestDifference = u32::MAX; let mut bestVersion = 0; for i in 0..VERSION_DECODE_INFO.len() as u32 { @@ -149,38 +175,80 @@ impl Version { // Top left finder pattern + separator + format bitMatrix.setRegion(0, 0, 9, 9)?; - // Top right finder pattern + separator + format - bitMatrix.setRegion(dimension - 8, 0, 8, 9)?; - // Bottom left finder pattern + separator + format - bitMatrix.setRegion(0, dimension - 8, 9, 8)?; - // Alignment patterns - let max = self.alignmentPatternCenters.len(); - for x in 0..max { - let i = self.alignmentPatternCenters[x] - 2; - for y in 0..max { - if (x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0) { - bitMatrix.setRegion(self.alignmentPatternCenters[y] - 2, i, 5, 5)?; + if !self.is_micro { + // Top right finder pattern + separator + format + bitMatrix.setRegion(dimension - 8, 0, 8, 9)?; + // Bottom left finder pattern + separator + format + bitMatrix.setRegion(0, dimension - 8, 9, 8)?; + + // Alignment patterns + let max = self.alignmentPatternCenters.len(); + for x in 0..max { + let i = self.alignmentPatternCenters[x] - 2; + for y in 0..max { + if (x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0) { + bitMatrix.setRegion(self.alignmentPatternCenters[y] - 2, i, 5, 5)?; + } + // else no o alignment patterns near the three finder patterns } - // else no o alignment patterns near the three finder patterns } - } - // Vertical timing pattern - bitMatrix.setRegion(6, 9, 1, dimension - 17)?; - // Horizontal timing pattern - bitMatrix.setRegion(9, 6, dimension - 17, 1)?; + // Vertical timing pattern + bitMatrix.setRegion(6, 9, 1, dimension - 17)?; + // Horizontal timing pattern + bitMatrix.setRegion(9, 6, dimension - 17, 1)?; - if self.versionNumber > 6 { - // Version info, top right - bitMatrix.setRegion(dimension - 11, 0, 3, 6)?; - // Version info, bottom left - bitMatrix.setRegion(0, dimension - 11, 6, 3)?; + if self.versionNumber > 6 { + // Version info, top right + bitMatrix.setRegion(dimension - 11, 0, 3, 6)?; + // Version info, bottom left + bitMatrix.setRegion(0, dimension - 11, 6, 3)?; + } + } else { + // Vertical timing pattern + bitMatrix.setRegion(9, 0, dimension - 9, 1)?; + + // Horizontal timing pattern + bitMatrix.setRegion(0, 9, 1, dimension - 9)?; } Ok(bitMatrix) } + pub fn build_micro_versions() -> Vec { + vec![ + Version::new_micro(1, vec![ECBlocks::new(2, vec![ECB::new(1, 3)])]), + Version::new_micro( + 2, + vec![ + ECBlocks::new(5, vec![ECB::new(1, 5)]), + ECBlocks::new(6, vec![ECB::new(1, 4)]), + ], + ), + Version::new_micro( + 3, + vec![ + ECBlocks::new(6, vec![ECB::new(1, 11)]), + ECBlocks::new(8, vec![ECB::new(1, 9)]), + ], + ), + Version::new_micro( + 4, + vec![ + ECBlocks::new(8, vec![ECB::new(1, 16)]), + ECBlocks::new(10, vec![ECB::new(1, 14)]), + ECBlocks::new(14, vec![ECB::new(1, 10)]), + ], + ), + ] + // static const Version allVersions[] = { + // {1, {2, 1, 3, 0, 0}}, + // {2, {5, 1, 5, 0, 0, 6, 1, 4, 0, 0}}, + // {3, {6, 1, 11, 0, 0, 8, 1, 9, 0, 0}}, + // {4, {8, 1, 16, 0, 0, 10, 1, 14, 0, 0, 14, 1, 10, 0, 0}}}; + } + /** * See ISO 18004:2006 6.5.1 Table 9 */ @@ -189,402 +257,402 @@ impl Version { Version::new( 1, Vec::from([]), - Vec::from([ + [ ECBlocks::new(7, Vec::from([ECB::new(1, 19)])), ECBlocks::new(10, Vec::from([ECB::new(1, 16)])), ECBlocks::new(13, Vec::from([ECB::new(1, 13)])), ECBlocks::new(17, Vec::from([ECB::new(1, 9)])), - ]), + ], ), Version::new( 2, Vec::from([6, 18]), - Vec::from([ + [ ECBlocks::new(10, Vec::from([ECB::new(1, 34)])), ECBlocks::new(16, Vec::from([ECB::new(1, 28)])), ECBlocks::new(22, Vec::from([ECB::new(1, 22)])), ECBlocks::new(28, Vec::from([ECB::new(1, 16)])), - ]), + ], ), Version::new( 3, Vec::from([6, 22]), - Vec::from([ + [ ECBlocks::new(15, Vec::from([ECB::new(1, 55)])), ECBlocks::new(26, Vec::from([ECB::new(1, 44)])), ECBlocks::new(18, Vec::from([ECB::new(2, 17)])), ECBlocks::new(22, Vec::from([ECB::new(2, 13)])), - ]), + ], ), Version::new( 4, Vec::from([6, 26]), - Vec::from([ + [ ECBlocks::new(20, Vec::from([ECB::new(1, 80)])), ECBlocks::new(18, Vec::from([ECB::new(2, 32)])), ECBlocks::new(26, Vec::from([ECB::new(2, 24)])), ECBlocks::new(16, Vec::from([ECB::new(4, 9)])), - ]), + ], ), Version::new( 5, Vec::from([6, 30]), - Vec::from([ + [ ECBlocks::new(26, Vec::from([ECB::new(1, 108)])), ECBlocks::new(24, Vec::from([ECB::new(2, 43)])), ECBlocks::new(18, Vec::from([ECB::new(2, 15), ECB::new(2, 16)])), ECBlocks::new(22, Vec::from([ECB::new(2, 11), ECB::new(2, 12)])), - ]), + ], ), Version::new( 6, Vec::from([6, 34]), - Vec::from([ + [ ECBlocks::new(18, Vec::from([ECB::new(2, 68)])), ECBlocks::new(16, Vec::from([ECB::new(4, 27)])), ECBlocks::new(24, Vec::from([ECB::new(4, 19)])), ECBlocks::new(28, Vec::from([ECB::new(4, 15)])), - ]), + ], ), Version::new( 7, Vec::from([6, 22, 38]), - Vec::from([ + [ ECBlocks::new(20, Vec::from([ECB::new(2, 78)])), ECBlocks::new(18, Vec::from([ECB::new(4, 31)])), ECBlocks::new(18, Vec::from([ECB::new(2, 14), ECB::new(4, 15)])), ECBlocks::new(26, Vec::from([ECB::new(4, 13), ECB::new(1, 14)])), - ]), + ], ), Version::new( 8, Vec::from([6, 24, 42]), - Vec::from([ + [ ECBlocks::new(24, Vec::from([ECB::new(2, 97)])), ECBlocks::new(22, Vec::from([ECB::new(2, 38), ECB::new(2, 39)])), ECBlocks::new(22, Vec::from([ECB::new(4, 18), ECB::new(2, 19)])), ECBlocks::new(26, Vec::from([ECB::new(4, 14), ECB::new(2, 15)])), - ]), + ], ), Version::new( 9, Vec::from([6, 26, 46]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(2, 116)])), ECBlocks::new(22, Vec::from([ECB::new(3, 36), ECB::new(2, 37)])), ECBlocks::new(20, Vec::from([ECB::new(4, 16), ECB::new(4, 17)])), ECBlocks::new(24, Vec::from([ECB::new(4, 12), ECB::new(4, 13)])), - ]), + ], ), Version::new( 10, Vec::from([6, 28, 50]), - Vec::from([ + [ ECBlocks::new(18, Vec::from([ECB::new(2, 68), ECB::new(2, 69)])), ECBlocks::new(26, Vec::from([ECB::new(4, 43), ECB::new(1, 44)])), ECBlocks::new(24, Vec::from([ECB::new(6, 19), ECB::new(2, 20)])), ECBlocks::new(28, Vec::from([ECB::new(6, 15), ECB::new(2, 16)])), - ]), + ], ), Version::new( 11, Vec::from([6, 30, 54]), - Vec::from([ + [ ECBlocks::new(20, Vec::from([ECB::new(4, 81)])), ECBlocks::new(30, Vec::from([ECB::new(1, 50), ECB::new(4, 51)])), ECBlocks::new(28, Vec::from([ECB::new(4, 22), ECB::new(4, 23)])), ECBlocks::new(24, Vec::from([ECB::new(3, 12), ECB::new(8, 13)])), - ]), + ], ), Version::new( 12, Vec::from([6, 32, 58]), - Vec::from([ + [ ECBlocks::new(24, Vec::from([ECB::new(2, 92), ECB::new(2, 93)])), ECBlocks::new(22, Vec::from([ECB::new(6, 36), ECB::new(2, 37)])), ECBlocks::new(26, Vec::from([ECB::new(4, 20), ECB::new(6, 21)])), ECBlocks::new(28, Vec::from([ECB::new(7, 14), ECB::new(4, 15)])), - ]), + ], ), Version::new( 13, Vec::from([6, 34, 62]), - Vec::from([ + [ ECBlocks::new(26, Vec::from([ECB::new(4, 107)])), ECBlocks::new(22, Vec::from([ECB::new(8, 37), ECB::new(1, 38)])), ECBlocks::new(24, Vec::from([ECB::new(8, 20), ECB::new(4, 21)])), ECBlocks::new(22, Vec::from([ECB::new(12, 11), ECB::new(4, 12)])), - ]), + ], ), Version::new( 14, Vec::from([6, 26, 46, 66]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(3, 115), ECB::new(1, 116)])), ECBlocks::new(24, Vec::from([ECB::new(4, 40), ECB::new(5, 41)])), ECBlocks::new(20, Vec::from([ECB::new(11, 16), ECB::new(5, 17)])), ECBlocks::new(24, Vec::from([ECB::new(11, 12), ECB::new(5, 13)])), - ]), + ], ), Version::new( 15, Vec::from([6, 26, 48, 70]), - Vec::from([ + [ ECBlocks::new(22, Vec::from([ECB::new(5, 87), ECB::new(1, 88)])), ECBlocks::new(24, Vec::from([ECB::new(5, 41), ECB::new(5, 42)])), ECBlocks::new(30, Vec::from([ECB::new(5, 24), ECB::new(7, 25)])), ECBlocks::new(24, Vec::from([ECB::new(11, 12), ECB::new(7, 13)])), - ]), + ], ), Version::new( 16, Vec::from([6, 26, 50, 74]), - Vec::from([ + [ ECBlocks::new(24, Vec::from([ECB::new(5, 98), ECB::new(1, 99)])), ECBlocks::new(28, Vec::from([ECB::new(7, 45), ECB::new(3, 46)])), ECBlocks::new(24, Vec::from([ECB::new(15, 19), ECB::new(2, 20)])), ECBlocks::new(30, Vec::from([ECB::new(3, 15), ECB::new(13, 16)])), - ]), + ], ), Version::new( 17, Vec::from([6, 30, 54, 78]), - Vec::from([ + [ ECBlocks::new(28, Vec::from([ECB::new(1, 107), ECB::new(5, 108)])), ECBlocks::new(28, Vec::from([ECB::new(10, 46), ECB::new(1, 47)])), ECBlocks::new(28, Vec::from([ECB::new(1, 22), ECB::new(15, 23)])), ECBlocks::new(28, Vec::from([ECB::new(2, 14), ECB::new(17, 15)])), - ]), + ], ), Version::new( 18, Vec::from([6, 30, 56, 82]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(5, 120), ECB::new(1, 121)])), ECBlocks::new(26, Vec::from([ECB::new(9, 43), ECB::new(4, 44)])), ECBlocks::new(28, Vec::from([ECB::new(17, 22), ECB::new(1, 23)])), ECBlocks::new(28, Vec::from([ECB::new(2, 14), ECB::new(19, 15)])), - ]), + ], ), Version::new( 19, Vec::from([6, 30, 58, 86]), - Vec::from([ + [ ECBlocks::new(28, Vec::from([ECB::new(3, 113), ECB::new(4, 114)])), ECBlocks::new(26, Vec::from([ECB::new(3, 44), ECB::new(11, 45)])), ECBlocks::new(26, Vec::from([ECB::new(17, 21), ECB::new(4, 22)])), ECBlocks::new(26, Vec::from([ECB::new(9, 13), ECB::new(16, 14)])), - ]), + ], ), Version::new( 20, Vec::from([6, 34, 62, 90]), - Vec::from([ + [ ECBlocks::new(28, Vec::from([ECB::new(3, 107), ECB::new(5, 108)])), ECBlocks::new(26, Vec::from([ECB::new(3, 41), ECB::new(13, 42)])), ECBlocks::new(30, Vec::from([ECB::new(15, 24), ECB::new(5, 25)])), ECBlocks::new(28, Vec::from([ECB::new(15, 15), ECB::new(10, 16)])), - ]), + ], ), Version::new( 21, Vec::from([6, 28, 50, 72, 94]), - Vec::from([ + [ ECBlocks::new(28, Vec::from([ECB::new(4, 116), ECB::new(4, 117)])), ECBlocks::new(26, Vec::from([ECB::new(17, 42)])), ECBlocks::new(28, Vec::from([ECB::new(17, 22), ECB::new(6, 23)])), ECBlocks::new(30, Vec::from([ECB::new(19, 16), ECB::new(6, 17)])), - ]), + ], ), Version::new( 22, Vec::from([6, 26, 50, 74, 98]), - Vec::from([ + [ ECBlocks::new(28, Vec::from([ECB::new(2, 111), ECB::new(7, 112)])), ECBlocks::new(28, Vec::from([ECB::new(17, 46)])), ECBlocks::new(30, Vec::from([ECB::new(7, 24), ECB::new(16, 25)])), ECBlocks::new(24, Vec::from([ECB::new(34, 13)])), - ]), + ], ), Version::new( 23, Vec::from([6, 30, 54, 78, 102]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(4, 121), ECB::new(5, 122)])), ECBlocks::new(28, Vec::from([ECB::new(4, 47), ECB::new(14, 48)])), ECBlocks::new(30, Vec::from([ECB::new(11, 24), ECB::new(14, 25)])), ECBlocks::new(30, Vec::from([ECB::new(16, 15), ECB::new(14, 16)])), - ]), + ], ), Version::new( 24, Vec::from([6, 28, 54, 80, 106]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(6, 117), ECB::new(4, 118)])), ECBlocks::new(28, Vec::from([ECB::new(6, 45), ECB::new(14, 46)])), ECBlocks::new(30, Vec::from([ECB::new(11, 24), ECB::new(16, 25)])), ECBlocks::new(30, Vec::from([ECB::new(30, 16), ECB::new(2, 17)])), - ]), + ], ), Version::new( 25, Vec::from([6, 32, 58, 84, 110]), - Vec::from([ + [ ECBlocks::new(26, Vec::from([ECB::new(8, 106), ECB::new(4, 107)])), ECBlocks::new(28, Vec::from([ECB::new(8, 47), ECB::new(13, 48)])), ECBlocks::new(30, Vec::from([ECB::new(7, 24), ECB::new(22, 25)])), ECBlocks::new(30, Vec::from([ECB::new(22, 15), ECB::new(13, 16)])), - ]), + ], ), Version::new( 26, Vec::from([6, 30, 58, 86, 114]), - Vec::from([ + [ ECBlocks::new(28, Vec::from([ECB::new(10, 114), ECB::new(2, 115)])), ECBlocks::new(28, Vec::from([ECB::new(19, 46), ECB::new(4, 47)])), ECBlocks::new(28, Vec::from([ECB::new(28, 22), ECB::new(6, 23)])), ECBlocks::new(30, Vec::from([ECB::new(33, 16), ECB::new(4, 17)])), - ]), + ], ), Version::new( 27, Vec::from([6, 34, 62, 90, 118]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(8, 122), ECB::new(4, 123)])), ECBlocks::new(28, Vec::from([ECB::new(22, 45), ECB::new(3, 46)])), ECBlocks::new(30, Vec::from([ECB::new(8, 23), ECB::new(26, 24)])), ECBlocks::new(30, Vec::from([ECB::new(12, 15), ECB::new(28, 16)])), - ]), + ], ), Version::new( 28, Vec::from([6, 26, 50, 74, 98, 122]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(3, 117), ECB::new(10, 118)])), ECBlocks::new(28, Vec::from([ECB::new(3, 45), ECB::new(23, 46)])), ECBlocks::new(30, Vec::from([ECB::new(4, 24), ECB::new(31, 25)])), ECBlocks::new(30, Vec::from([ECB::new(11, 15), ECB::new(31, 16)])), - ]), + ], ), Version::new( 29, Vec::from([6, 30, 54, 78, 102, 126]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(7, 116), ECB::new(7, 117)])), ECBlocks::new(28, Vec::from([ECB::new(21, 45), ECB::new(7, 46)])), ECBlocks::new(30, Vec::from([ECB::new(1, 23), ECB::new(37, 24)])), ECBlocks::new(30, Vec::from([ECB::new(19, 15), ECB::new(26, 16)])), - ]), + ], ), Version::new( 30, Vec::from([6, 26, 52, 78, 104, 130]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(5, 115), ECB::new(10, 116)])), ECBlocks::new(28, Vec::from([ECB::new(19, 47), ECB::new(10, 48)])), ECBlocks::new(30, Vec::from([ECB::new(15, 24), ECB::new(25, 25)])), ECBlocks::new(30, Vec::from([ECB::new(23, 15), ECB::new(25, 16)])), - ]), + ], ), Version::new( 31, Vec::from([6, 30, 56, 82, 108, 134]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(13, 115), ECB::new(3, 116)])), ECBlocks::new(28, Vec::from([ECB::new(2, 46), ECB::new(29, 47)])), ECBlocks::new(30, Vec::from([ECB::new(42, 24), ECB::new(1, 25)])), ECBlocks::new(30, Vec::from([ECB::new(23, 15), ECB::new(28, 16)])), - ]), + ], ), Version::new( 32, Vec::from([6, 34, 60, 86, 112, 138]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(17, 115)])), ECBlocks::new(28, Vec::from([ECB::new(10, 46), ECB::new(23, 47)])), ECBlocks::new(30, Vec::from([ECB::new(10, 24), ECB::new(35, 25)])), ECBlocks::new(30, Vec::from([ECB::new(19, 15), ECB::new(35, 16)])), - ]), + ], ), Version::new( 33, Vec::from([6, 30, 58, 86, 114, 142]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(17, 115), ECB::new(1, 116)])), ECBlocks::new(28, Vec::from([ECB::new(14, 46), ECB::new(21, 47)])), ECBlocks::new(30, Vec::from([ECB::new(29, 24), ECB::new(19, 25)])), ECBlocks::new(30, Vec::from([ECB::new(11, 15), ECB::new(46, 16)])), - ]), + ], ), Version::new( 34, Vec::from([6, 34, 62, 90, 118, 146]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(13, 115), ECB::new(6, 116)])), ECBlocks::new(28, Vec::from([ECB::new(14, 46), ECB::new(23, 47)])), ECBlocks::new(30, Vec::from([ECB::new(44, 24), ECB::new(7, 25)])), ECBlocks::new(30, Vec::from([ECB::new(59, 16), ECB::new(1, 17)])), - ]), + ], ), Version::new( 35, Vec::from([6, 30, 54, 78, 102, 126, 150]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(12, 121), ECB::new(7, 122)])), ECBlocks::new(28, Vec::from([ECB::new(12, 47), ECB::new(26, 48)])), ECBlocks::new(30, Vec::from([ECB::new(39, 24), ECB::new(14, 25)])), ECBlocks::new(30, Vec::from([ECB::new(22, 15), ECB::new(41, 16)])), - ]), + ], ), Version::new( 36, Vec::from([6, 24, 50, 76, 102, 128, 154]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(6, 121), ECB::new(14, 122)])), ECBlocks::new(28, Vec::from([ECB::new(6, 47), ECB::new(34, 48)])), ECBlocks::new(30, Vec::from([ECB::new(46, 24), ECB::new(10, 25)])), ECBlocks::new(30, Vec::from([ECB::new(2, 15), ECB::new(64, 16)])), - ]), + ], ), Version::new( 37, Vec::from([6, 28, 54, 80, 106, 132, 158]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(17, 122), ECB::new(4, 123)])), ECBlocks::new(28, Vec::from([ECB::new(29, 46), ECB::new(14, 47)])), ECBlocks::new(30, Vec::from([ECB::new(49, 24), ECB::new(10, 25)])), ECBlocks::new(30, Vec::from([ECB::new(24, 15), ECB::new(46, 16)])), - ]), + ], ), Version::new( 38, Vec::from([6, 32, 58, 84, 110, 136, 162]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(4, 122), ECB::new(18, 123)])), ECBlocks::new(28, Vec::from([ECB::new(13, 46), ECB::new(32, 47)])), ECBlocks::new(30, Vec::from([ECB::new(48, 24), ECB::new(14, 25)])), ECBlocks::new(30, Vec::from([ECB::new(42, 15), ECB::new(32, 16)])), - ]), + ], ), Version::new( 39, Vec::from([6, 26, 54, 82, 110, 138, 166]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(20, 117), ECB::new(4, 118)])), ECBlocks::new(28, Vec::from([ECB::new(40, 47), ECB::new(7, 48)])), ECBlocks::new(30, Vec::from([ECB::new(43, 24), ECB::new(22, 25)])), ECBlocks::new(30, Vec::from([ECB::new(10, 15), ECB::new(67, 16)])), - ]), + ], ), Version::new( 40, Vec::from([6, 30, 58, 86, 114, 142, 170]), - Vec::from([ + [ ECBlocks::new(30, Vec::from([ECB::new(19, 118), ECB::new(6, 119)])), ECBlocks::new(28, Vec::from([ECB::new(18, 47), ECB::new(31, 48)])), ECBlocks::new(30, Vec::from([ECB::new(34, 24), ECB::new(34, 25)])), ECBlocks::new(30, Vec::from([ECB::new(20, 15), ECB::new(61, 16)])), - ]), + ], ), ]) /* new Version(4, new int[]{6, 26}, @@ -916,7 +984,7 @@ impl fmt::Display for Version { * each set of blocks. It also holds the number of error-correction codewords per block since it * will be the same across all blocks within one version.

*/ -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ECBlocks { ecCodewordsPerBlock: u32, ecBlocks: Vec, @@ -930,7 +998,7 @@ impl ECBlocks { } } - pub fn getECCodewordsPerBlock(&self) -> u32 { + pub const fn getECCodewordsPerBlock(&self) -> u32 { self.ecCodewordsPerBlock } @@ -956,7 +1024,7 @@ impl ECBlocks { * This includes the number of data codewords, and the number of times a block with these * parameters is used consecutively in the QR code version's format.

*/ -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct ECB { count: u32, dataCodewords: u32, @@ -970,11 +1038,11 @@ impl ECB { } } - pub fn getCount(&self) -> u32 { + pub const fn getCount(&self) -> u32 { self.count } - pub fn getDataCodewords(&self) -> u32 { + pub const fn getDataCodewords(&self) -> u32 { self.dataCodewords } } diff --git a/src/qrcode/detector/qrcode_detector.rs b/src/qrcode/detector/qrcode_detector.rs index da16f3e..fde699b 100644 --- a/src/qrcode/detector/qrcode_detector.rs +++ b/src/qrcode/detector/qrcode_detector.rs @@ -218,12 +218,13 @@ impl<'a> Detector<'_> { dimension: u32, ) -> Result { let sampler = DefaultGridSampler::default(); - sampler.sample_grid( + let (res, _) = sampler.sample_grid( image, dimension, dimension, &[SamplerControl::new(dimension, dimension, transform)], - ) + )?; + Ok(res) } /** diff --git a/src/qrcode/mod.rs b/src/qrcode/mod.rs index be88824..ae308d6 100644 --- a/src/qrcode/mod.rs +++ b/src/qrcode/mod.rs @@ -8,6 +8,8 @@ pub use qr_code_reader::*; mod qr_code_writer; pub use qr_code_writer::*; +pub mod cpp_port; + #[cfg(test)] #[cfg(feature = "image")] mod QRCodeWriterTestCase; diff --git a/src/rxing_result.rs b/src/rxing_result.rs index ea49223..f888935 100644 --- a/src/rxing_result.rs +++ b/src/rxing_result.rs @@ -16,7 +16,10 @@ use std::{collections::HashMap, fmt}; -use crate::{BarcodeFormat, Point, RXingResultMetadataType, RXingResultMetadataValue}; +use crate::{ + common::cpp_essentials::DecoderResult, BarcodeFormat, MetadataDictionary, Point, + RXingResultMetadataType, RXingResultMetadataValue, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -95,6 +98,44 @@ impl RXingResult { } } + pub fn with_decoder_result( + res: DecoderResult, + resultPoints: &[Point], + format: BarcodeFormat, + ) -> Self + where + T: Copy + Clone + Default + Eq + PartialEq, + { + let mut new_res = Self::new( + &res.text(), + res.content().bytes().to_vec(), + resultPoints.to_vec(), + format, + ); + + let mut meta_data = MetadataDictionary::new(); + meta_data.insert( + RXingResultMetadataType::ERROR_CORRECTION_LEVEL, + RXingResultMetadataValue::ErrorCorrectionLevel(res.ecLevel().to_owned()), + ); + meta_data.insert( + RXingResultMetadataType::STRUCTURED_APPEND_PARITY, + RXingResultMetadataValue::StructuredAppendParity(res.structuredAppend().count), + ); + meta_data.insert( + RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE, + RXingResultMetadataValue::StructuredAppendSequence(res.structuredAppend().index), + ); + meta_data.insert( + RXingResultMetadataType::SYMBOLOGY_IDENTIFIER, + RXingResultMetadataValue::SymbologyIdentifier(res.symbologyIdentifier()), + ); + + new_res.putAllMetadata(meta_data); + + new_res + } + /** * @return raw text encoded by the barcode */ diff --git a/src/rxing_result_metadata.rs b/src/rxing_result_metadata.rs index 58ac8c9..700a501 100644 --- a/src/rxing_result_metadata.rs +++ b/src/rxing_result_metadata.rs @@ -112,6 +112,8 @@ pub enum RXingResultMetadataType { IS_MIRRORED, CONTENT_TYPE, + + IS_INVERTED, } impl From for RXingResultMetadataType { @@ -120,7 +122,7 @@ impl From for RXingResultMetadataType { "OTHER" => RXingResultMetadataType::OTHER, "ORIENTATION" => RXingResultMetadataType::ORIENTATION, "BYTE_SEGMENTS" | "BYTESEGMENTS" => RXingResultMetadataType::BYTE_SEGMENTS, - "ERROR_CORRECTION_LEVEL" | "ERRORCORRECTIONLEVEL" => { + "ERROR_CORRECTION_LEVEL" | "ERRORCORRECTIONLEVEL" | "ECLEVEL" => { RXingResultMetadataType::ERROR_CORRECTION_LEVEL } "ISSUE_NUMBER" | "ISSUENUMBER" => RXingResultMetadataType::ISSUE_NUMBER, @@ -141,6 +143,7 @@ impl From for RXingResultMetadataType { } "IS_MIRRORED" | "ISMIRRORED" => RXingResultMetadataType::IS_MIRRORED, "CONTENT_TYPE" | "CONTENTTYPE" => RXingResultMetadataType::CONTENT_TYPE, + "ISINVERTED" => RXingResultMetadataType::IS_INVERTED, _ => RXingResultMetadataType::OTHER, } } @@ -229,4 +232,6 @@ pub enum RXingResultMetadataValue { IsMirrored(bool), ContentType(String), + + IsInverted(bool), } diff --git a/src/rxing_result_point.rs b/src/rxing_result_point.rs index 1e8d28b..069d8ad 100644 --- a/src/rxing_result_point.rs +++ b/src/rxing_result_point.rs @@ -25,6 +25,14 @@ pub fn point(x: f32, y: f32) -> Point { Point::new(x, y) } +pub fn point_g>(x: T, y: T) -> Option { + Some(Point::new(x.try_into().ok()?, y.try_into().ok()?)) +} + +pub fn point_i>(x: T, y: T) -> Point { + Point::new(x.into() as f32, y.into() as f32) +} + /** Currently necessary because the external OneDReader proc macro uses it. */ pub type RXingResultPoint = Point; @@ -117,6 +125,22 @@ impl std::ops::Add for Point { } } +impl std::ops::Add for Point { + type Output = Self; + + fn add(self, rhs: f32) -> Self::Output { + Self::new(self.x + rhs, self.y + rhs) + } +} + +impl std::ops::Add for f32 { + type Output = Point; + + fn add(self, rhs: Point) -> Self::Output { + Point::new(rhs.x + self, rhs.y + self) + } +} + impl std::ops::Mul for Point { type Output = Self; @@ -141,6 +165,14 @@ impl std::ops::Mul for Point { } } +impl std::ops::Mul for Point { + type Output = Self; + + fn mul(self, rhs: u32) -> Self::Output { + Self::new(self.x * rhs as f32, self.y * rhs as f32) + } +} + impl std::ops::Mul for i32 { type Output = Point; @@ -165,6 +197,14 @@ impl std::ops::Div for Point { } } +impl std::ops::Mul for u32 { + type Output = Point; + + fn mul(self, rhs: Point) -> Self::Output { + Self::Output::new(rhs.x * self as f32, rhs.y * self as f32) + } +} + impl Point { pub fn dot(self, p: Self) -> f32 { self.x * p.x + self.y * p.y @@ -240,6 +280,13 @@ impl Point { y: self.y.round(), } } + + pub fn floor(self) -> Self { + Self { + x: self.x.floor(), + y: self.y.floor(), + } + } } impl From<&(f32, f32)> for Point { @@ -260,6 +307,12 @@ impl From<(i32, i32)> for Point { } } +impl From<(u32, u32)> for Point { + fn from(value: (u32, u32)) -> Self { + Self::new(value.0 as f32, value.1 as f32) + } +} + #[cfg(test)] mod tests { use super::Point; diff --git a/test_resources/blackbox/cpp/microqrcode-1/1.png b/test_resources/blackbox/cpp/microqrcode-1/1.png new file mode 100644 index 0000000..35c855d Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/1.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/1.txt b/test_resources/blackbox/cpp/microqrcode-1/1.txt new file mode 100644 index 0000000..121eab0 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/1.txt @@ -0,0 +1 @@ +Wikipedia \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/11.jpg b/test_resources/blackbox/cpp/microqrcode-1/11.jpg new file mode 100644 index 0000000..bfff33f Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/11.jpg differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/11.txt b/test_resources/blackbox/cpp/microqrcode-1/11.txt new file mode 100644 index 0000000..64209c1 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/11.txt @@ -0,0 +1 @@ +Loser \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/12.jpg b/test_resources/blackbox/cpp/microqrcode-1/12.jpg new file mode 100644 index 0000000..4724b6c Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/12.jpg differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/12.txt b/test_resources/blackbox/cpp/microqrcode-1/12.txt new file mode 100644 index 0000000..7d3298d --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/12.txt @@ -0,0 +1 @@ +SN888623311 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/3.jpg b/test_resources/blackbox/cpp/microqrcode-1/3.jpg new file mode 100644 index 0000000..9a984dd Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/3.jpg differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/3.txt b/test_resources/blackbox/cpp/microqrcode-1/3.txt new file mode 100644 index 0000000..b513fa6 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/3.txt @@ -0,0 +1 @@ +malgajninto \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/7.jpg b/test_resources/blackbox/cpp/microqrcode-1/7.jpg new file mode 100644 index 0000000..d92a678 Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/7.jpg differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/7.txt b/test_resources/blackbox/cpp/microqrcode-1/7.txt new file mode 100644 index 0000000..ae75576 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/7.txt @@ -0,0 +1 @@ +Victus \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/9.jpg b/test_resources/blackbox/cpp/microqrcode-1/9.jpg new file mode 100644 index 0000000..682f4d1 Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/9.jpg differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/9.txt b/test_resources/blackbox/cpp/microqrcode-1/9.txt new file mode 100644 index 0000000..8dd8a14 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/9.txt @@ -0,0 +1 @@ +ezik \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/M1-Numeric.png b/test_resources/blackbox/cpp/microqrcode-1/M1-Numeric.png new file mode 100644 index 0000000..8a7d18a Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/M1-Numeric.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/M1-Numeric.txt b/test_resources/blackbox/cpp/microqrcode-1/M1-Numeric.txt new file mode 100644 index 0000000..bd41cba --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/M1-Numeric.txt @@ -0,0 +1 @@ +12345 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/M2-Alpha.png b/test_resources/blackbox/cpp/microqrcode-1/M2-Alpha.png new file mode 100644 index 0000000..eefdaa3 Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/M2-Alpha.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/M2-Alpha.txt b/test_resources/blackbox/cpp/microqrcode-1/M2-Alpha.txt new file mode 100644 index 0000000..48b83b8 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/M2-Alpha.txt @@ -0,0 +1 @@ +ABC \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/M2-Numeric.png b/test_resources/blackbox/cpp/microqrcode-1/M2-Numeric.png new file mode 100644 index 0000000..ea9810f Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/M2-Numeric.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/M2-Numeric.txt b/test_resources/blackbox/cpp/microqrcode-1/M2-Numeric.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/M2-Numeric.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/M3-Binary.png b/test_resources/blackbox/cpp/microqrcode-1/M3-Binary.png new file mode 100644 index 0000000..889b4a3 Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/M3-Binary.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/M3-Binary.txt b/test_resources/blackbox/cpp/microqrcode-1/M3-Binary.txt new file mode 100644 index 0000000..29f3003 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/M3-Binary.txt @@ -0,0 +1 @@ +E=mc² \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/M3-Kanji.png b/test_resources/blackbox/cpp/microqrcode-1/M3-Kanji.png new file mode 100644 index 0000000..d8d0679 Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/M3-Kanji.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/M3-Kanji.txt b/test_resources/blackbox/cpp/microqrcode-1/M3-Kanji.txt new file mode 100644 index 0000000..b40554e --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/M3-Kanji.txt @@ -0,0 +1 @@ +誠 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/M3-Mixed.png b/test_resources/blackbox/cpp/microqrcode-1/M3-Mixed.png new file mode 100644 index 0000000..5ae95b6 Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/M3-Mixed.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/M3-Mixed.txt b/test_resources/blackbox/cpp/microqrcode-1/M3-Mixed.txt new file mode 100644 index 0000000..070ee25 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/M3-Mixed.txt @@ -0,0 +1 @@ +1234 ABCD \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/M4-Binary.png b/test_resources/blackbox/cpp/microqrcode-1/M4-Binary.png new file mode 100644 index 0000000..8f4ca80 Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/M4-Binary.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/M4-Binary.txt b/test_resources/blackbox/cpp/microqrcode-1/M4-Binary.txt new file mode 100644 index 0000000..35aefb1 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/M4-Binary.txt @@ -0,0 +1 @@ +!"§$%&/()=?` \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/M4-Mixed.png b/test_resources/blackbox/cpp/microqrcode-1/M4-Mixed.png new file mode 100644 index 0000000..e26ea01 Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/M4-Mixed.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/M4-Mixed.txt b/test_resources/blackbox/cpp/microqrcode-1/M4-Mixed.txt new file mode 100644 index 0000000..34cbb77 --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/M4-Mixed.txt @@ -0,0 +1 @@ +1234 ABCD abcd \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/MQR-needs-br-update.jpg b/test_resources/blackbox/cpp/microqrcode-1/MQR-needs-br-update.jpg new file mode 100644 index 0000000..26eddef Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/MQR-needs-br-update.jpg differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/MQR-needs-br-update.txt b/test_resources/blackbox/cpp/microqrcode-1/MQR-needs-br-update.txt new file mode 100644 index 0000000..222d0af --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/MQR-needs-br-update.txt @@ -0,0 +1 @@ +Micro QR Code \ No newline at end of file diff --git a/test_resources/blackbox/cpp/microqrcode-1/NoQuietZone.png b/test_resources/blackbox/cpp/microqrcode-1/NoQuietZone.png new file mode 100644 index 0000000..f7a917d Binary files /dev/null and b/test_resources/blackbox/cpp/microqrcode-1/NoQuietZone.png differ diff --git a/test_resources/blackbox/cpp/microqrcode-1/NoQuietZone.txt b/test_resources/blackbox/cpp/microqrcode-1/NoQuietZone.txt new file mode 100644 index 0000000..bd41cba --- /dev/null +++ b/test_resources/blackbox/cpp/microqrcode-1/NoQuietZone.txt @@ -0,0 +1 @@ +12345 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/1.png b/test_resources/blackbox/cpp/qrcode-1/1.png new file mode 100644 index 0000000..0e75f9b Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/1.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/1.txt b/test_resources/blackbox/cpp/qrcode-1/1.txt new file mode 100644 index 0000000..cb6b805 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/1.txt @@ -0,0 +1 @@ +MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/11.png b/test_resources/blackbox/cpp/qrcode-1/11.png new file mode 100644 index 0000000..ccec513 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/11.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/11.txt b/test_resources/blackbox/cpp/qrcode-1/11.txt new file mode 100644 index 0000000..cb6b805 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/11.txt @@ -0,0 +1 @@ +MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/12.png b/test_resources/blackbox/cpp/qrcode-1/12.png new file mode 100644 index 0000000..cb281e9 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/12.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/12.txt b/test_resources/blackbox/cpp/qrcode-1/12.txt new file mode 100644 index 0000000..cb6b805 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/12.txt @@ -0,0 +1 @@ +MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/13.png b/test_resources/blackbox/cpp/qrcode-1/13.png new file mode 100644 index 0000000..7592844 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/13.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/13.txt b/test_resources/blackbox/cpp/qrcode-1/13.txt new file mode 100644 index 0000000..c6adb03 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/13.txt @@ -0,0 +1 @@ +http://google.com/gwt/n?u=bluenile.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/14.png b/test_resources/blackbox/cpp/qrcode-1/14.png new file mode 100644 index 0000000..cecd1fe Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/14.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/14.txt b/test_resources/blackbox/cpp/qrcode-1/14.txt new file mode 100644 index 0000000..c6adb03 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/14.txt @@ -0,0 +1 @@ +http://google.com/gwt/n?u=bluenile.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/15.png b/test_resources/blackbox/cpp/qrcode-1/15.png new file mode 100644 index 0000000..96a8d5f Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/15.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/15.txt b/test_resources/blackbox/cpp/qrcode-1/15.txt new file mode 100644 index 0000000..c6adb03 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/15.txt @@ -0,0 +1 @@ +http://google.com/gwt/n?u=bluenile.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/16.png b/test_resources/blackbox/cpp/qrcode-1/16.png new file mode 100644 index 0000000..cd69ba9 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/16.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/16.txt b/test_resources/blackbox/cpp/qrcode-1/16.txt new file mode 100644 index 0000000..31a244c --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/16.txt @@ -0,0 +1,4 @@ +Sean Owen +srowen@google.com +917-364-2918 +http://awesome-thoughts.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/17.png b/test_resources/blackbox/cpp/qrcode-1/17.png new file mode 100644 index 0000000..b2af901 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/17.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/17.txt b/test_resources/blackbox/cpp/qrcode-1/17.txt new file mode 100644 index 0000000..31a244c --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/17.txt @@ -0,0 +1,4 @@ +Sean Owen +srowen@google.com +917-364-2918 +http://awesome-thoughts.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/18.png b/test_resources/blackbox/cpp/qrcode-1/18.png new file mode 100644 index 0000000..c427f3b Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/18.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/18.txt b/test_resources/blackbox/cpp/qrcode-1/18.txt new file mode 100644 index 0000000..31a244c --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/18.txt @@ -0,0 +1,4 @@ +Sean Owen +srowen@google.com +917-364-2918 +http://awesome-thoughts.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/19.png b/test_resources/blackbox/cpp/qrcode-1/19.png new file mode 100644 index 0000000..551a216 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/19.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/19.txt b/test_resources/blackbox/cpp/qrcode-1/19.txt new file mode 100644 index 0000000..31a244c --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/19.txt @@ -0,0 +1,4 @@ +Sean Owen +srowen@google.com +917-364-2918 +http://awesome-thoughts.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/2.png b/test_resources/blackbox/cpp/qrcode-1/2.png new file mode 100644 index 0000000..3b84c5f Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/2.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/2.txt b/test_resources/blackbox/cpp/qrcode-1/2.txt new file mode 100644 index 0000000..cb6b805 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/2.txt @@ -0,0 +1 @@ +MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/20.png b/test_resources/blackbox/cpp/qrcode-1/20.png new file mode 100644 index 0000000..fee84a0 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/20.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/20.txt b/test_resources/blackbox/cpp/qrcode-1/20.txt new file mode 100644 index 0000000..31a244c --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/20.txt @@ -0,0 +1,4 @@ +Sean Owen +srowen@google.com +917-364-2918 +http://awesome-thoughts.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/4.png b/test_resources/blackbox/cpp/qrcode-1/4.png new file mode 100644 index 0000000..e5f5b26 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/4.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/4.txt b/test_resources/blackbox/cpp/qrcode-1/4.txt new file mode 100644 index 0000000..cb6b805 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/4.txt @@ -0,0 +1 @@ +MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/7.png b/test_resources/blackbox/cpp/qrcode-1/7.png new file mode 100644 index 0000000..51ed21a Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/7.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/7.txt b/test_resources/blackbox/cpp/qrcode-1/7.txt new file mode 100644 index 0000000..cb6b805 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/7.txt @@ -0,0 +1 @@ +MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/8.png b/test_resources/blackbox/cpp/qrcode-1/8.png new file mode 100644 index 0000000..19ab129 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/8.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/8.txt b/test_resources/blackbox/cpp/qrcode-1/8.txt new file mode 100644 index 0000000..cb6b805 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/8.txt @@ -0,0 +1 @@ +MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-1/9.png b/test_resources/blackbox/cpp/qrcode-1/9.png new file mode 100644 index 0000000..41c0d4a Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-1/9.png differ diff --git a/test_resources/blackbox/cpp/qrcode-1/9.txt b/test_resources/blackbox/cpp/qrcode-1/9.txt new file mode 100644 index 0000000..cb6b805 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-1/9.txt @@ -0,0 +1 @@ +MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/#128.png b/test_resources/blackbox/cpp/qrcode-2/#128.png new file mode 100644 index 0000000..c3aaad5 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/#128.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/#128.txt b/test_resources/blackbox/cpp/qrcode-2/#128.txt new file mode 100644 index 0000000..d8b15ed --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/#128.txt @@ -0,0 +1 @@ +M$K0YGV0G \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/#1314.jpg b/test_resources/blackbox/cpp/qrcode-2/#1314.jpg new file mode 100644 index 0000000..34eeacc Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/#1314.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/#1314.txt b/test_resources/blackbox/cpp/qrcode-2/#1314.txt new file mode 100644 index 0000000..7dde3a9 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/#1314.txt @@ -0,0 +1 @@ +ehjfjoijewofijweoijfoiwejfoiwejfoi \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/#142.jpg b/test_resources/blackbox/cpp/qrcode-2/#142.jpg new file mode 100644 index 0000000..3291c7b Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/#142.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/#142.txt b/test_resources/blackbox/cpp/qrcode-2/#142.txt new file mode 100644 index 0000000..043a653 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/#142.txt @@ -0,0 +1 @@ +vmess://eyJhZGQiOiJ6cHh1LnNhZGZ3d3cuY2YiLCJhaWQiOjIzMywiaG9zdCI6InpweHUuc2FkZnd3dy5jZiIsImlkIjoiZTcwNGYxZjgtOTBlZS00NzIwLTljMjktMzY2ZjY2YTM5ODVmIiwibmV0Ijoid3MiLCJwYXRoIjoiLyIsInBvcnQiOjQ0MywicHMiOiJ6cHh1LnNhZGZ3d3cuY2YtWzQ0M10tdm1lc3MiLCJ0bHMiOiJ0bHMiLCJ0eXBlIjoibm9uZSIsInYiOjJ9 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/#199.jpg b/test_resources/blackbox/cpp/qrcode-2/#199.jpg new file mode 100644 index 0000000..6c47a0b Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/#199.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/#199.txt b/test_resources/blackbox/cpp/qrcode-2/#199.txt new file mode 100644 index 0000000..651b6fe --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/#199.txt @@ -0,0 +1 @@ +ABCDEF1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/#199b.jpg b/test_resources/blackbox/cpp/qrcode-2/#199b.jpg new file mode 100644 index 0000000..8d4cafb Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/#199b.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/#199b.txt b/test_resources/blackbox/cpp/qrcode-2/#199b.txt new file mode 100644 index 0000000..5c103c8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/#199b.txt @@ -0,0 +1 @@ +abcdefghijklmnopqrstuvwxyz012345 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/#253.jpg b/test_resources/blackbox/cpp/qrcode-2/#253.jpg new file mode 100644 index 0000000..7bc934f Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/#253.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/#253.txt b/test_resources/blackbox/cpp/qrcode-2/#253.txt new file mode 100644 index 0000000..066646f --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/#253.txt @@ -0,0 +1 @@ +http://cli.im \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/#258.jpg b/test_resources/blackbox/cpp/qrcode-2/#258.jpg new file mode 100644 index 0000000..0aa8fa9 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/#258.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/#258.txt b/test_resources/blackbox/cpp/qrcode-2/#258.txt new file mode 100644 index 0000000..ff0b6a8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/#258.txt @@ -0,0 +1 @@ +https://vt.tiktok.com/ZGJUXpy5e/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/1.png b/test_resources/blackbox/cpp/qrcode-2/1.png new file mode 100644 index 0000000..9924161 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/1.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/1.result.txt b/test_resources/blackbox/cpp/qrcode-2/1.result.txt new file mode 100644 index 0000000..ee6eefe --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/1.result.txt @@ -0,0 +1,2 @@ +symbologyIdentifier=]Q1 +ecLevel=M diff --git a/test_resources/blackbox/cpp/qrcode-2/1.txt b/test_resources/blackbox/cpp/qrcode-2/1.txt new file mode 100644 index 0000000..58493bd --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/1.txt @@ -0,0 +1 @@ +When we at WRT talk about \"text,\" we are generally talking about a particular kind of readable information encoding - and readable is a complex proposition. Text may be stylized in a way we are unfamiliar with, as in blackletter - it may be interspersed with some markup we don\'t understand, such as HTML - it may be be a substitution system we aren\'t familiar with, such as braille or morse code - or it may be a system that, while technically human-readable, isn\'t particularly optimized for reading by humans, as with barcodes (although barcodes can be read). \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/10.png b/test_resources/blackbox/cpp/qrcode-2/10.png new file mode 100644 index 0000000..e9ee685 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/10.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/10.txt b/test_resources/blackbox/cpp/qrcode-2/10.txt new file mode 100644 index 0000000..14632d9 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/10.txt @@ -0,0 +1,2 @@ +Google モバイル +http://google.jp \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/11.png b/test_resources/blackbox/cpp/qrcode-2/11.png new file mode 100644 index 0000000..e28c7a0 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/11.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/11.txt b/test_resources/blackbox/cpp/qrcode-2/11.txt new file mode 100644 index 0000000..2b574b7 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/11.txt @@ -0,0 +1,12 @@ +BEGIN:VCARD +N:Kennedy;Steve +TEL:+44 (0)7775 755503 +ADR;HOME:;;Flat 2, 43 Howitt Road, Belsize Park;London;;NW34LU;UK +ORG:NetTek Ltd; +TITLE:Consultant +EMAIL:steve@nettek.co.uk +URL:www.nettek.co.uk +EMAIL;IM:MSN:steve@gbnet.net +NOTE:Testing 1 2 3 +BDAY:19611105 +END:VCARD \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/12.png b/test_resources/blackbox/cpp/qrcode-2/12.png new file mode 100644 index 0000000..d9271ea Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/12.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/12.txt b/test_resources/blackbox/cpp/qrcode-2/12.txt new file mode 100644 index 0000000..6c62da9 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/12.txt @@ -0,0 +1 @@ +The 2005 USGS aerial photography of the Washington Monument is censored. \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/13.png b/test_resources/blackbox/cpp/qrcode-2/13.png new file mode 100644 index 0000000..12848cf Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/13.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/13.txt b/test_resources/blackbox/cpp/qrcode-2/13.txt new file mode 100644 index 0000000..6c62da9 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/13.txt @@ -0,0 +1 @@ +The 2005 USGS aerial photography of the Washington Monument is censored. \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/14.png b/test_resources/blackbox/cpp/qrcode-2/14.png new file mode 100644 index 0000000..3b666c5 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/14.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/14.txt b/test_resources/blackbox/cpp/qrcode-2/14.txt new file mode 100644 index 0000000..454a60e --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/14.txt @@ -0,0 +1 @@ +http://bbc.co.uk/programmes \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/15.png b/test_resources/blackbox/cpp/qrcode-2/15.png new file mode 100644 index 0000000..dcdf221 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/15.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/15.txt b/test_resources/blackbox/cpp/qrcode-2/15.txt new file mode 100644 index 0000000..e6b5a97 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/15.txt @@ -0,0 +1 @@ +In 25 words or less in the comments, below, tell us how QR codes will make the world less ordinary. \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/16.png b/test_resources/blackbox/cpp/qrcode-2/16.png new file mode 100644 index 0000000..afffda2 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/16.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/16.txt b/test_resources/blackbox/cpp/qrcode-2/16.txt new file mode 100644 index 0000000..bc80f71 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/16.txt @@ -0,0 +1,4 @@ +[内側QRコード] + +*ダブルQR* +http://d-qr.net/ex/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/17.png b/test_resources/blackbox/cpp/qrcode-2/17.png new file mode 100644 index 0000000..822366a Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/17.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/17.txt b/test_resources/blackbox/cpp/qrcode-2/17.txt new file mode 100644 index 0000000..7fba436 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/17.txt @@ -0,0 +1,2 @@ +デザインQR +http://d-qr.net/ex/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/18.png b/test_resources/blackbox/cpp/qrcode-2/18.png new file mode 100644 index 0000000..2e7d551 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/18.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/18.txt b/test_resources/blackbox/cpp/qrcode-2/18.txt new file mode 100644 index 0000000..beb8901 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/18.txt @@ -0,0 +1,2 @@ +*デザインQR* +http://d-qr.net/ex/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/19.png b/test_resources/blackbox/cpp/qrcode-2/19.png new file mode 100644 index 0000000..3133082 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/19.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/19.txt b/test_resources/blackbox/cpp/qrcode-2/19.txt new file mode 100644 index 0000000..88f2165 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/19.txt @@ -0,0 +1,2 @@ +*デザインQR* +http://d-qr.net/ex/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/2.png b/test_resources/blackbox/cpp/qrcode-2/2.png new file mode 100644 index 0000000..18f1f8a Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/2.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/2.txt b/test_resources/blackbox/cpp/qrcode-2/2.txt new file mode 100644 index 0000000..38d534e --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/2.txt @@ -0,0 +1 @@ +LANDBYASCARC PERCEPTIBLEC EEK,OOZI GITSWAYTHROU AWILDERNESSOFBESUPPOSED,I CANT,ORATL ASTDWARFISH.NO REESOFANYM NITUDEARETOBESOMEMISERA EFRAMEBUIL I GS,TENANTED, U INGSUMMER THEFUGITIVE;BUTTHEWHO ISLAND,WI H EXCEPTIONOFT W STERNPOI ,ANDALINEOFLLIAMLEGR .HEWASOF N ENTHUGUENOTF Y ANDHADON BEENWEALTHTIONCONSE ENTUPONH SD STERS,HELEFTNE LE NS,THEC OFHISFOREOUTHCARO A.THISISLA AVERYSINGULARO TCONSISTSO ITTLEELSEDSAQUART FAMILE. TI PARATEDFROMTHEMA AN BYASCAR YPERCEPTERESORT MARSH EN EVEGETATION,ASMIGH SU POSED, CANT,ORATREMITY, ORT OULTRIESTANDS,ANDWHEREARESOM MIS FRAMEBUIFEVER,MAYBE INDEED, TO;BUTT EISLAND,WITNYYEARSAGO,IC ACTED LLIAM AND.HEWASOFANNESHADREDUCEDHIM OWA IONC NSEQUENTUPONHISDSIDENCEATSULLIVA \'S HC ROLINA.THISISLANOUTTHR LESLON . THATN OINTE U RTEROF E.ITISTHROU ERNE DSANDSLI ,AFAVOR TOFT HEN.T \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/20.png b/test_resources/blackbox/cpp/qrcode-2/20.png new file mode 100644 index 0000000..364a155 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/20.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/20.txt b/test_resources/blackbox/cpp/qrcode-2/20.txt new file mode 100644 index 0000000..d88f5eb --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/20.txt @@ -0,0 +1,2 @@ +*デザインQR* +http://d-qr.net/ex/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/21.png b/test_resources/blackbox/cpp/qrcode-2/21.png new file mode 100644 index 0000000..41923a1 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/21.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/21.txt b/test_resources/blackbox/cpp/qrcode-2/21.txt new file mode 100644 index 0000000..09c84c7 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/21.txt @@ -0,0 +1,2 @@ +*デザインQR* +http://d-qr.net/ex/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/22.png b/test_resources/blackbox/cpp/qrcode-2/22.png new file mode 100644 index 0000000..e05dbb4 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/22.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/22.txt b/test_resources/blackbox/cpp/qrcode-2/22.txt new file mode 100644 index 0000000..eeaa3a7 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/22.txt @@ -0,0 +1 @@ +http://www.hotpepper.jp/mobile/cgi-bin/MBLC80100.cgi?SA=00&Z=AG&vos=hpp064&uid=NULLGWDOCOMO \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/23.png b/test_resources/blackbox/cpp/qrcode-2/23.png new file mode 100644 index 0000000..5481d9b Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/23.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/23.txt b/test_resources/blackbox/cpp/qrcode-2/23.txt new file mode 100644 index 0000000..c7cd5be --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/23.txt @@ -0,0 +1 @@ +http://aniful.jp/pr/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/24.png b/test_resources/blackbox/cpp/qrcode-2/24.png new file mode 100644 index 0000000..db460eb Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/24.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/24.txt b/test_resources/blackbox/cpp/qrcode-2/24.txt new file mode 100644 index 0000000..41bcbc1 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/24.txt @@ -0,0 +1,2 @@ +*デザインQR* +http://d-qr.net/ex/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/25.png b/test_resources/blackbox/cpp/qrcode-2/25.png new file mode 100644 index 0000000..1a4a8bc Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/25.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/25.txt b/test_resources/blackbox/cpp/qrcode-2/25.txt new file mode 100644 index 0000000..2742bc1 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/25.txt @@ -0,0 +1 @@ +MEBKM:TITLE:;URL:http://d.kaywa.com/2020400102;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/26.png b/test_resources/blackbox/cpp/qrcode-2/26.png new file mode 100644 index 0000000..adaa93c Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/26.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/26.txt b/test_resources/blackbox/cpp/qrcode-2/26.txt new file mode 100644 index 0000000..c4f62fe --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/26.txt @@ -0,0 +1,3 @@ +<デザインQR> +イラスト入りカラーQRコード +http://d-qr.net/ex/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/27.png b/test_resources/blackbox/cpp/qrcode-2/27.png new file mode 100644 index 0000000..aecc7c4 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/27.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/27.txt b/test_resources/blackbox/cpp/qrcode-2/27.txt new file mode 100644 index 0000000..bb0c693 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/27.txt @@ -0,0 +1,2 @@ +*デザインQR* +http://d-qr.net/ex/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/28.png b/test_resources/blackbox/cpp/qrcode-2/28.png new file mode 100644 index 0000000..c58767f Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/28.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/28.txt b/test_resources/blackbox/cpp/qrcode-2/28.txt new file mode 100644 index 0000000..c44369e --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/28.txt @@ -0,0 +1 @@ +http://www.webtech.co.jp/k/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/29.png b/test_resources/blackbox/cpp/qrcode-2/29.png new file mode 100644 index 0000000..1d28157 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/29.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/29.txt b/test_resources/blackbox/cpp/qrcode-2/29.txt new file mode 100644 index 0000000..87ad299 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/29.txt @@ -0,0 +1,3 @@ +http://live.fdgm.jp/u/event/hype/hype_top.html + +MEBKM:TITLE:hypeモバイル;URL:http\://live.fdgm.jp/u/event/hype/hype_top.html;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/30.png b/test_resources/blackbox/cpp/qrcode-2/30.png new file mode 100644 index 0000000..9c51d26 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/30.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/30.txt b/test_resources/blackbox/cpp/qrcode-2/30.txt new file mode 100644 index 0000000..bc64130 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/30.txt @@ -0,0 +1 @@ +MECARD:N:測試;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/30a.png b/test_resources/blackbox/cpp/qrcode-2/30a.png new file mode 100644 index 0000000..d2ba7cb Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/30a.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/30a.txt b/test_resources/blackbox/cpp/qrcode-2/30a.txt new file mode 100644 index 0000000..bc64130 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/30a.txt @@ -0,0 +1 @@ +MECARD:N:測試;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/31.png b/test_resources/blackbox/cpp/qrcode-2/31.png new file mode 100644 index 0000000..abfcb66 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/31.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/31.txt b/test_resources/blackbox/cpp/qrcode-2/31.txt new file mode 100644 index 0000000..59d2e9d --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/31.txt @@ -0,0 +1 @@ +今度のバージョンでは文章の暗号化ができます。 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/32.png b/test_resources/blackbox/cpp/qrcode-2/32.png new file mode 100644 index 0000000..524ae54 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/32.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/32.txt b/test_resources/blackbox/cpp/qrcode-2/32.txt new file mode 100644 index 0000000..2b574b7 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/32.txt @@ -0,0 +1,12 @@ +BEGIN:VCARD +N:Kennedy;Steve +TEL:+44 (0)7775 755503 +ADR;HOME:;;Flat 2, 43 Howitt Road, Belsize Park;London;;NW34LU;UK +ORG:NetTek Ltd; +TITLE:Consultant +EMAIL:steve@nettek.co.uk +URL:www.nettek.co.uk +EMAIL;IM:MSN:steve@gbnet.net +NOTE:Testing 1 2 3 +BDAY:19611105 +END:VCARD \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/33.png b/test_resources/blackbox/cpp/qrcode-2/33.png new file mode 100644 index 0000000..d5be5d9 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/33.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/33.txt b/test_resources/blackbox/cpp/qrcode-2/33.txt new file mode 100644 index 0000000..a8d077e --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/33.txt @@ -0,0 +1 @@ +AD:SUB:阿;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/34.png b/test_resources/blackbox/cpp/qrcode-2/34.png new file mode 100644 index 0000000..1284efc Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/34.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/34.txt b/test_resources/blackbox/cpp/qrcode-2/34.txt new file mode 100644 index 0000000..d83e8aa --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/34.txt @@ -0,0 +1 @@ +http://www.google.com/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/35.png b/test_resources/blackbox/cpp/qrcode-2/35.png new file mode 100644 index 0000000..03af8ed Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/35.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/35.txt b/test_resources/blackbox/cpp/qrcode-2/35.txt new file mode 100644 index 0000000..d83e8aa --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/35.txt @@ -0,0 +1 @@ +http://www.google.com/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/4.png b/test_resources/blackbox/cpp/qrcode-2/4.png new file mode 100644 index 0000000..891608f Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/4.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/4.txt b/test_resources/blackbox/cpp/qrcode-2/4.txt new file mode 100644 index 0000000..dd36eda --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/4.txt @@ -0,0 +1 @@ +http://wwws.keihin.ktr.mlit.go.jp/keitai/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/5.png b/test_resources/blackbox/cpp/qrcode-2/5.png new file mode 100644 index 0000000..899e7e6 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/5.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/5.txt b/test_resources/blackbox/cpp/qrcode-2/5.txt new file mode 100644 index 0000000..9a35ecd --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/5.txt @@ -0,0 +1 @@ +2021200000 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/6.png b/test_resources/blackbox/cpp/qrcode-2/6.png new file mode 100644 index 0000000..107e00c Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/6.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/6.txt b/test_resources/blackbox/cpp/qrcode-2/6.txt new file mode 100644 index 0000000..87c34b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/6.txt @@ -0,0 +1 @@ +http://d.kaywa.com/20207100 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/7.png b/test_resources/blackbox/cpp/qrcode-2/7.png new file mode 100644 index 0000000..892a1c5 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/7.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/7.txt b/test_resources/blackbox/cpp/qrcode-2/7.txt new file mode 100644 index 0000000..b8c822d --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/7.txt @@ -0,0 +1 @@ +BIZCARD:N:Todd;X:Ogasawara;T:Tech Geek;C:MobileViews.com;A:MobileTown USA;E:editor@mobileviews.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/8.png b/test_resources/blackbox/cpp/qrcode-2/8.png new file mode 100644 index 0000000..e3f032c Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/8.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/8.txt b/test_resources/blackbox/cpp/qrcode-2/8.txt new file mode 100644 index 0000000..9e0a7f8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/8.txt @@ -0,0 +1 @@ +http://staticrooster.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/9.png b/test_resources/blackbox/cpp/qrcode-2/9.png new file mode 100644 index 0000000..ebf66bf Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/9.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/9.txt b/test_resources/blackbox/cpp/qrcode-2/9.txt new file mode 100644 index 0000000..292aea1 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/9.txt @@ -0,0 +1 @@ +Morden \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/estimate-tilt.jpg b/test_resources/blackbox/cpp/qrcode-2/estimate-tilt.jpg new file mode 100644 index 0000000..e30b300 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/estimate-tilt.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/estimate-tilt.txt b/test_resources/blackbox/cpp/qrcode-2/estimate-tilt.txt new file mode 100644 index 0000000..a837ad3 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/estimate-tilt.txt @@ -0,0 +1 @@ +VERSION 1 6CM \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/fix-finderpattern-order.jpg b/test_resources/blackbox/cpp/qrcode-2/fix-finderpattern-order.jpg new file mode 100644 index 0000000..0250a0e Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/fix-finderpattern-order.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/fix-finderpattern-order.txt b/test_resources/blackbox/cpp/qrcode-2/fix-finderpattern-order.txt new file mode 100644 index 0000000..97268c5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/fix-finderpattern-order.txt @@ -0,0 +1 @@ +VERSION 2 8CM \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/fix-traceline.jpg b/test_resources/blackbox/cpp/qrcode-2/fix-traceline.jpg new file mode 100644 index 0000000..4bea7b8 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/fix-traceline.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/fix-traceline.txt b/test_resources/blackbox/cpp/qrcode-2/fix-traceline.txt new file mode 100644 index 0000000..97268c5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/fix-traceline.txt @@ -0,0 +1 @@ +VERSION 2 8CM \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/gs1-figure-4.15.1-2.metadata.txt b/test_resources/blackbox/cpp/qrcode-2/gs1-figure-4.15.1-2.metadata.txt new file mode 100644 index 0000000..06b3826 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/gs1-figure-4.15.1-2.metadata.txt @@ -0,0 +1,2 @@ +symbologyIdentifier=]Q3 +ecLevel=M diff --git a/test_resources/blackbox/cpp/qrcode-2/gs1-figure-4.15.1-2.png b/test_resources/blackbox/cpp/qrcode-2/gs1-figure-4.15.1-2.png new file mode 100644 index 0000000..d5d83a0 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/gs1-figure-4.15.1-2.png differ diff --git a/test_resources/blackbox/cpp/qrcode-2/gs1-figure-4.15.1-2.txt b/test_resources/blackbox/cpp/qrcode-2/gs1-figure-4.15.1-2.txt new file mode 100644 index 0000000..b2c7f13 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/gs1-figure-4.15.1-2.txt @@ -0,0 +1 @@ +01095040000591012112345678p901101234567p171411208200http://www.gs1.org/demo/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/high-res-1.jpg b/test_resources/blackbox/cpp/qrcode-2/high-res-1.jpg new file mode 100644 index 0000000..69efb1f Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/high-res-1.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/high-res-1.txt b/test_resources/blackbox/cpp/qrcode-2/high-res-1.txt new file mode 100644 index 0000000..5071ea8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/high-res-1.txt @@ -0,0 +1,2 @@ +QR code (abbreviated from Quick Response Code) is the trademark for a type of matrix barcode (or two-dimensional barcode) first designed in 1994 for the automotive industry in Japan.[1] A barcode is a machine-readable optical label that contains information about the item to which it is attached. In practice, QR codes often contain data for a locator, identifier, or tracker that points to a website or application. A QR code uses four standardized encoding modes (numeric, alphanumeric, byte/binary, and kanji) to store data efficiently; extensions may also be used.[2]sdfgksjdflkgjsdkfgiotmbx,cmvbofghjoaasdfaERYYKLLDFGSDFFFFFFFFFFFFFFFFFFFFFFFFDDDDDDDDDSVFB094856JLKSJFGS0DBIUZKL;KSFDF09846JLKSDNFGBLDKSFBHJ0SP98ASDFKthat contains information about the item to which it is attached. In practice, QR codes often contain data for a locator, identifier, or trac +QR code (abbreviated from Quick Response Code) is the trademark for a type of matrix barcode (or two-dimensional barcode) first designed in 1994 for the automotive industry in Japan.[1] A barcode is a machine-readable optical label that contains information about the item to which it is attached. In practice, QR codes often contain data for a locator, identifier, or tracker that points to a website or application. A QR code uses four standardized encoding modes (numeric, alphanumeric, byte/binary, and kanji) to store data efficiently; extensions may also be used.[2]sdfgksjdflkgjsdkfgiotmbx,cmvbofghjoaasdfaERYYKLLDFGSDFFFFFFFFFFFFFFFFFFFFFFFFDDDDDDDDDSVFB094856JLKSJFGS0DBIUZKL;KSFDF09846JLKSDNFGBLDKSFBHJ0SP98ASDFKthat contains information about the item to which it is attached. In practice, QR codes often contain data for a locator, identifier, or trac \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/qr-inv-1.jpg b/test_resources/blackbox/cpp/qrcode-2/qr-inv-1.jpg new file mode 100644 index 0000000..f76de9e Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/qr-inv-1.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/qr-inv-1.metadata.txt-removed b/test_resources/blackbox/cpp/qrcode-2/qr-inv-1.metadata.txt-removed new file mode 100644 index 0000000..da82284 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/qr-inv-1.metadata.txt-removed @@ -0,0 +1 @@ +isInverted=true diff --git a/test_resources/blackbox/cpp/qrcode-2/qr-inv-1.txt b/test_resources/blackbox/cpp/qrcode-2/qr-inv-1.txt new file mode 100644 index 0000000..a1d7336 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/qr-inv-1.txt @@ -0,0 +1 @@ +https://beeline.co/pages/app \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-2/qr-inv-2.jpg b/test_resources/blackbox/cpp/qrcode-2/qr-inv-2.jpg new file mode 100644 index 0000000..dd20ead Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-2/qr-inv-2.jpg differ diff --git a/test_resources/blackbox/cpp/qrcode-2/qr-inv-2.txt b/test_resources/blackbox/cpp/qrcode-2/qr-inv-2.txt new file mode 100644 index 0000000..cb6b805 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/qr-inv-2.txt @@ -0,0 +1 @@ +MEBKM:URL:http\://en.wikipedia.org/wiki/Main_Page;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/01.png b/test_resources/blackbox/cpp/qrcode-3/01.png new file mode 100644 index 0000000..1acbf4c Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/01.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/01.txt b/test_resources/blackbox/cpp/qrcode-3/01.txt new file mode 100644 index 0000000..4960897 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/01.txt @@ -0,0 +1 @@ +http://arnaud.sahuguet.com/graffiti/test.php?ll=-74.00309961503218,40.74102573163046,0 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/03.png b/test_resources/blackbox/cpp/qrcode-3/03.png new file mode 100644 index 0000000..996695e Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/03.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/03.txt b/test_resources/blackbox/cpp/qrcode-3/03.txt new file mode 100644 index 0000000..4960897 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/03.txt @@ -0,0 +1 @@ +http://arnaud.sahuguet.com/graffiti/test.php?ll=-74.00309961503218,40.74102573163046,0 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/04.png b/test_resources/blackbox/cpp/qrcode-3/04.png new file mode 100644 index 0000000..1906189 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/04.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/04.txt b/test_resources/blackbox/cpp/qrcode-3/04.txt new file mode 100644 index 0000000..4960897 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/04.txt @@ -0,0 +1 @@ +http://arnaud.sahuguet.com/graffiti/test.php?ll=-74.00309961503218,40.74102573163046,0 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/05.png b/test_resources/blackbox/cpp/qrcode-3/05.png new file mode 100644 index 0000000..6eb83f4 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/05.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/05.txt b/test_resources/blackbox/cpp/qrcode-3/05.txt new file mode 100644 index 0000000..4960897 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/05.txt @@ -0,0 +1 @@ +http://arnaud.sahuguet.com/graffiti/test.php?ll=-74.00309961503218,40.74102573163046,0 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/07.png b/test_resources/blackbox/cpp/qrcode-3/07.png new file mode 100644 index 0000000..acc18c0 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/07.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/07.txt b/test_resources/blackbox/cpp/qrcode-3/07.txt new file mode 100644 index 0000000..4960897 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/07.txt @@ -0,0 +1 @@ +http://arnaud.sahuguet.com/graffiti/test.php?ll=-74.00309961503218,40.74102573163046,0 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/09.png b/test_resources/blackbox/cpp/qrcode-3/09.png new file mode 100644 index 0000000..d78db9a Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/09.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/09.txt b/test_resources/blackbox/cpp/qrcode-3/09.txt new file mode 100644 index 0000000..4960897 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/09.txt @@ -0,0 +1 @@ +http://arnaud.sahuguet.com/graffiti/test.php?ll=-74.00309961503218,40.74102573163046,0 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/10.png b/test_resources/blackbox/cpp/qrcode-3/10.png new file mode 100644 index 0000000..bd12f9e Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/10.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/10.txt b/test_resources/blackbox/cpp/qrcode-3/10.txt new file mode 100644 index 0000000..352576d --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/10.txt @@ -0,0 +1 @@ +MECARD:N:Google 411,;TEL:18665881077;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/11.png b/test_resources/blackbox/cpp/qrcode-3/11.png new file mode 100644 index 0000000..e5aa885 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/11.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/11.txt b/test_resources/blackbox/cpp/qrcode-3/11.txt new file mode 100644 index 0000000..352576d --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/11.txt @@ -0,0 +1 @@ +MECARD:N:Google 411,;TEL:18665881077;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/13.png b/test_resources/blackbox/cpp/qrcode-3/13.png new file mode 100644 index 0000000..8b6e292 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/13.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/13.txt b/test_resources/blackbox/cpp/qrcode-3/13.txt new file mode 100644 index 0000000..352576d --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/13.txt @@ -0,0 +1 @@ +MECARD:N:Google 411,;TEL:18665881077;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/14.png b/test_resources/blackbox/cpp/qrcode-3/14.png new file mode 100644 index 0000000..96dcceb Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/14.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/14.txt b/test_resources/blackbox/cpp/qrcode-3/14.txt new file mode 100644 index 0000000..352576d --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/14.txt @@ -0,0 +1 @@ +MECARD:N:Google 411,;TEL:18665881077;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/15.png b/test_resources/blackbox/cpp/qrcode-3/15.png new file mode 100644 index 0000000..7a88b51 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/15.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/15.txt b/test_resources/blackbox/cpp/qrcode-3/15.txt new file mode 100644 index 0000000..352576d --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/15.txt @@ -0,0 +1 @@ +MECARD:N:Google 411,;TEL:18665881077;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/16.png b/test_resources/blackbox/cpp/qrcode-3/16.png new file mode 100644 index 0000000..d91e0c8 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/16.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/16.txt b/test_resources/blackbox/cpp/qrcode-3/16.txt new file mode 100644 index 0000000..352576d --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/16.txt @@ -0,0 +1 @@ +MECARD:N:Google 411,;TEL:18665881077;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/17.png b/test_resources/blackbox/cpp/qrcode-3/17.png new file mode 100644 index 0000000..021a605 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/17.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/17.txt b/test_resources/blackbox/cpp/qrcode-3/17.txt new file mode 100644 index 0000000..352576d --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/17.txt @@ -0,0 +1 @@ +MECARD:N:Google 411,;TEL:18665881077;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/18.png b/test_resources/blackbox/cpp/qrcode-3/18.png new file mode 100644 index 0000000..c50f3c9 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/18.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/18.txt b/test_resources/blackbox/cpp/qrcode-3/18.txt new file mode 100644 index 0000000..44a23fd --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/18.txt @@ -0,0 +1,2 @@ +UI office hours signup +http://www.corp.google.com/sparrow/ui_office_hours/ diff --git a/test_resources/blackbox/cpp/qrcode-3/19.png b/test_resources/blackbox/cpp/qrcode-3/19.png new file mode 100644 index 0000000..ec89304 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/19.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/19.txt b/test_resources/blackbox/cpp/qrcode-3/19.txt new file mode 100644 index 0000000..44a23fd --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/19.txt @@ -0,0 +1,2 @@ +UI office hours signup +http://www.corp.google.com/sparrow/ui_office_hours/ diff --git a/test_resources/blackbox/cpp/qrcode-3/21.png b/test_resources/blackbox/cpp/qrcode-3/21.png new file mode 100644 index 0000000..93b90d7 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/21.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/21.txt b/test_resources/blackbox/cpp/qrcode-3/21.txt new file mode 100644 index 0000000..44a23fd --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/21.txt @@ -0,0 +1,2 @@ +UI office hours signup +http://www.corp.google.com/sparrow/ui_office_hours/ diff --git a/test_resources/blackbox/cpp/qrcode-3/22.png b/test_resources/blackbox/cpp/qrcode-3/22.png new file mode 100644 index 0000000..6592168 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/22.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/22.txt b/test_resources/blackbox/cpp/qrcode-3/22.txt new file mode 100644 index 0000000..44a23fd --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/22.txt @@ -0,0 +1,2 @@ +UI office hours signup +http://www.corp.google.com/sparrow/ui_office_hours/ diff --git a/test_resources/blackbox/cpp/qrcode-3/23.png b/test_resources/blackbox/cpp/qrcode-3/23.png new file mode 100644 index 0000000..009b6d9 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/23.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/23.txt b/test_resources/blackbox/cpp/qrcode-3/23.txt new file mode 100644 index 0000000..44a23fd --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/23.txt @@ -0,0 +1,2 @@ +UI office hours signup +http://www.corp.google.com/sparrow/ui_office_hours/ diff --git a/test_resources/blackbox/cpp/qrcode-3/24.png b/test_resources/blackbox/cpp/qrcode-3/24.png new file mode 100644 index 0000000..f2c870c Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/24.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/24.txt b/test_resources/blackbox/cpp/qrcode-3/24.txt new file mode 100644 index 0000000..44a23fd --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/24.txt @@ -0,0 +1,2 @@ +UI office hours signup +http://www.corp.google.com/sparrow/ui_office_hours/ diff --git a/test_resources/blackbox/cpp/qrcode-3/25.png b/test_resources/blackbox/cpp/qrcode-3/25.png new file mode 100644 index 0000000..32ecfd4 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/25.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/25.txt b/test_resources/blackbox/cpp/qrcode-3/25.txt new file mode 100644 index 0000000..44a23fd --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/25.txt @@ -0,0 +1,2 @@ +UI office hours signup +http://www.corp.google.com/sparrow/ui_office_hours/ diff --git a/test_resources/blackbox/cpp/qrcode-3/27.png b/test_resources/blackbox/cpp/qrcode-3/27.png new file mode 100644 index 0000000..7e9a1e0 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/27.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/27.txt b/test_resources/blackbox/cpp/qrcode-3/27.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/27.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/28.png b/test_resources/blackbox/cpp/qrcode-3/28.png new file mode 100644 index 0000000..e6a605d Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/28.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/28.txt b/test_resources/blackbox/cpp/qrcode-3/28.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/28.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/30.png b/test_resources/blackbox/cpp/qrcode-3/30.png new file mode 100644 index 0000000..eeffa89 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/30.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/30.txt b/test_resources/blackbox/cpp/qrcode-3/30.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/30.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/31.png b/test_resources/blackbox/cpp/qrcode-3/31.png new file mode 100644 index 0000000..7e8f60d Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/31.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/31.txt b/test_resources/blackbox/cpp/qrcode-3/31.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/31.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/33.png b/test_resources/blackbox/cpp/qrcode-3/33.png new file mode 100644 index 0000000..d808e0b Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/33.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/33.txt b/test_resources/blackbox/cpp/qrcode-3/33.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/33.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/37.png b/test_resources/blackbox/cpp/qrcode-3/37.png new file mode 100644 index 0000000..d33307d Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/37.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/37.txt b/test_resources/blackbox/cpp/qrcode-3/37.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/37.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/40.png b/test_resources/blackbox/cpp/qrcode-3/40.png new file mode 100644 index 0000000..242d532 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/40.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/40.txt b/test_resources/blackbox/cpp/qrcode-3/40.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/40.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-3/42.png b/test_resources/blackbox/cpp/qrcode-3/42.png new file mode 100644 index 0000000..bb78795 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-3/42.png differ diff --git a/test_resources/blackbox/cpp/qrcode-3/42.txt b/test_resources/blackbox/cpp/qrcode-3/42.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-3/42.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/01.png b/test_resources/blackbox/cpp/qrcode-4/01.png new file mode 100644 index 0000000..af9c3be Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/01.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/01.txt b/test_resources/blackbox/cpp/qrcode-4/01.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/01.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/02.png b/test_resources/blackbox/cpp/qrcode-4/02.png new file mode 100644 index 0000000..69786f5 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/02.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/02.txt b/test_resources/blackbox/cpp/qrcode-4/02.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/02.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/03.png b/test_resources/blackbox/cpp/qrcode-4/03.png new file mode 100644 index 0000000..f515b95 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/03.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/03.txt b/test_resources/blackbox/cpp/qrcode-4/03.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/03.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/04.png b/test_resources/blackbox/cpp/qrcode-4/04.png new file mode 100644 index 0000000..292e6c3 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/04.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/04.txt b/test_resources/blackbox/cpp/qrcode-4/04.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/04.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/05.png b/test_resources/blackbox/cpp/qrcode-4/05.png new file mode 100644 index 0000000..726d416 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/05.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/05.txt b/test_resources/blackbox/cpp/qrcode-4/05.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/05.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/06.png b/test_resources/blackbox/cpp/qrcode-4/06.png new file mode 100644 index 0000000..2dce25b Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/06.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/06.txt b/test_resources/blackbox/cpp/qrcode-4/06.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/06.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/07.png b/test_resources/blackbox/cpp/qrcode-4/07.png new file mode 100644 index 0000000..86e2bb9 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/07.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/07.txt b/test_resources/blackbox/cpp/qrcode-4/07.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/07.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/08.png b/test_resources/blackbox/cpp/qrcode-4/08.png new file mode 100644 index 0000000..994be96 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/08.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/08.txt b/test_resources/blackbox/cpp/qrcode-4/08.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/08.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/09.png b/test_resources/blackbox/cpp/qrcode-4/09.png new file mode 100644 index 0000000..2e6b712 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/09.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/09.txt b/test_resources/blackbox/cpp/qrcode-4/09.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/09.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/10.png b/test_resources/blackbox/cpp/qrcode-4/10.png new file mode 100644 index 0000000..4964eec Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/10.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/10.txt b/test_resources/blackbox/cpp/qrcode-4/10.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/10.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/11.png b/test_resources/blackbox/cpp/qrcode-4/11.png new file mode 100644 index 0000000..b0e8ac2 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/11.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/11.txt b/test_resources/blackbox/cpp/qrcode-4/11.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/11.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/12.png b/test_resources/blackbox/cpp/qrcode-4/12.png new file mode 100644 index 0000000..3c77693 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/12.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/12.txt b/test_resources/blackbox/cpp/qrcode-4/12.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/12.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/13.png b/test_resources/blackbox/cpp/qrcode-4/13.png new file mode 100644 index 0000000..f272635 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/13.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/13.txt b/test_resources/blackbox/cpp/qrcode-4/13.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/13.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/14.png b/test_resources/blackbox/cpp/qrcode-4/14.png new file mode 100644 index 0000000..957461e Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/14.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/14.txt b/test_resources/blackbox/cpp/qrcode-4/14.txt new file mode 100644 index 0000000..f94af84 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/14.txt @@ -0,0 +1 @@ +Google Print Ads - T.G.I.A.F. - January 31, 2008 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/15.png b/test_resources/blackbox/cpp/qrcode-4/15.png new file mode 100644 index 0000000..1267fe8 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/15.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/15.txt b/test_resources/blackbox/cpp/qrcode-4/15.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/15.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/16.png b/test_resources/blackbox/cpp/qrcode-4/16.png new file mode 100644 index 0000000..32561ec Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/16.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/16.txt b/test_resources/blackbox/cpp/qrcode-4/16.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/16.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/17.png b/test_resources/blackbox/cpp/qrcode-4/17.png new file mode 100644 index 0000000..ba5e8bb Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/17.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/17.txt b/test_resources/blackbox/cpp/qrcode-4/17.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/17.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/18.png b/test_resources/blackbox/cpp/qrcode-4/18.png new file mode 100644 index 0000000..e029d19 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/18.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/18.txt b/test_resources/blackbox/cpp/qrcode-4/18.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/18.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/19.png b/test_resources/blackbox/cpp/qrcode-4/19.png new file mode 100644 index 0000000..614d918 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/19.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/19.txt b/test_resources/blackbox/cpp/qrcode-4/19.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/19.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/20.png b/test_resources/blackbox/cpp/qrcode-4/20.png new file mode 100644 index 0000000..b9ffc64 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/20.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/20.txt b/test_resources/blackbox/cpp/qrcode-4/20.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/20.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/21.png b/test_resources/blackbox/cpp/qrcode-4/21.png new file mode 100644 index 0000000..a08f282 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/21.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/21.txt b/test_resources/blackbox/cpp/qrcode-4/21.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/21.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/22.png b/test_resources/blackbox/cpp/qrcode-4/22.png new file mode 100644 index 0000000..e3c80d6 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/22.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/22.txt b/test_resources/blackbox/cpp/qrcode-4/22.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/22.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/23.png b/test_resources/blackbox/cpp/qrcode-4/23.png new file mode 100644 index 0000000..89d33a4 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/23.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/23.txt b/test_resources/blackbox/cpp/qrcode-4/23.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/23.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/24.png b/test_resources/blackbox/cpp/qrcode-4/24.png new file mode 100644 index 0000000..0fb5adc Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/24.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/24.txt b/test_resources/blackbox/cpp/qrcode-4/24.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/24.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/26.png b/test_resources/blackbox/cpp/qrcode-4/26.png new file mode 100644 index 0000000..5c7f418 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/26.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/26.txt b/test_resources/blackbox/cpp/qrcode-4/26.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/26.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/27.png b/test_resources/blackbox/cpp/qrcode-4/27.png new file mode 100644 index 0000000..668a20b Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/27.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/27.txt b/test_resources/blackbox/cpp/qrcode-4/27.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/27.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/28.png b/test_resources/blackbox/cpp/qrcode-4/28.png new file mode 100644 index 0000000..dfcdc67 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/28.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/28.txt b/test_resources/blackbox/cpp/qrcode-4/28.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/28.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/29.png b/test_resources/blackbox/cpp/qrcode-4/29.png new file mode 100644 index 0000000..70e8184 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/29.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/29.txt b/test_resources/blackbox/cpp/qrcode-4/29.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/29.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/30.png b/test_resources/blackbox/cpp/qrcode-4/30.png new file mode 100644 index 0000000..cd4f45e Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/30.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/30.txt b/test_resources/blackbox/cpp/qrcode-4/30.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/30.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/31.png b/test_resources/blackbox/cpp/qrcode-4/31.png new file mode 100644 index 0000000..5dd1460 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/31.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/31.txt b/test_resources/blackbox/cpp/qrcode-4/31.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/31.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/32.png b/test_resources/blackbox/cpp/qrcode-4/32.png new file mode 100644 index 0000000..7fb36dc Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/32.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/32.txt b/test_resources/blackbox/cpp/qrcode-4/32.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/32.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/33.png b/test_resources/blackbox/cpp/qrcode-4/33.png new file mode 100644 index 0000000..822e8be Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/33.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/33.txt b/test_resources/blackbox/cpp/qrcode-4/33.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/33.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/34.png b/test_resources/blackbox/cpp/qrcode-4/34.png new file mode 100644 index 0000000..1354c29 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/34.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/34.txt b/test_resources/blackbox/cpp/qrcode-4/34.txt new file mode 100644 index 0000000..1469db2 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/34.txt @@ -0,0 +1 @@ +http://code.google.com \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/36.png b/test_resources/blackbox/cpp/qrcode-4/36.png new file mode 100644 index 0000000..d751a31 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/36.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/36.txt b/test_resources/blackbox/cpp/qrcode-4/36.txt new file mode 100644 index 0000000..41872c8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/36.txt @@ -0,0 +1 @@ +http://code.google.com/p/zxing/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/39.png b/test_resources/blackbox/cpp/qrcode-4/39.png new file mode 100644 index 0000000..85625c8 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/39.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/39.txt b/test_resources/blackbox/cpp/qrcode-4/39.txt new file mode 100644 index 0000000..41872c8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/39.txt @@ -0,0 +1 @@ +http://code.google.com/p/zxing/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/40.png b/test_resources/blackbox/cpp/qrcode-4/40.png new file mode 100644 index 0000000..d72b7fb Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/40.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/40.txt b/test_resources/blackbox/cpp/qrcode-4/40.txt new file mode 100644 index 0000000..41872c8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/40.txt @@ -0,0 +1 @@ +http://code.google.com/p/zxing/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/41.png b/test_resources/blackbox/cpp/qrcode-4/41.png new file mode 100644 index 0000000..eb6f84f Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/41.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/41.txt b/test_resources/blackbox/cpp/qrcode-4/41.txt new file mode 100644 index 0000000..41872c8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/41.txt @@ -0,0 +1 @@ +http://code.google.com/p/zxing/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/42.png b/test_resources/blackbox/cpp/qrcode-4/42.png new file mode 100644 index 0000000..8f4a88f Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/42.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/42.txt b/test_resources/blackbox/cpp/qrcode-4/42.txt new file mode 100644 index 0000000..41872c8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/42.txt @@ -0,0 +1 @@ +http://code.google.com/p/zxing/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/43.png b/test_resources/blackbox/cpp/qrcode-4/43.png new file mode 100644 index 0000000..c8ba53e Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/43.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/43.txt b/test_resources/blackbox/cpp/qrcode-4/43.txt new file mode 100644 index 0000000..41872c8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/43.txt @@ -0,0 +1 @@ +http://code.google.com/p/zxing/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/46.png b/test_resources/blackbox/cpp/qrcode-4/46.png new file mode 100644 index 0000000..7bfd15e Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/46.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/46.txt b/test_resources/blackbox/cpp/qrcode-4/46.txt new file mode 100644 index 0000000..41872c8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/46.txt @@ -0,0 +1 @@ +http://code.google.com/p/zxing/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-4/48.png b/test_resources/blackbox/cpp/qrcode-4/48.png new file mode 100644 index 0000000..323ca12 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-4/48.png differ diff --git a/test_resources/blackbox/cpp/qrcode-4/48.txt b/test_resources/blackbox/cpp/qrcode-4/48.txt new file mode 100644 index 0000000..41872c8 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-4/48.txt @@ -0,0 +1 @@ +http://code.google.com/p/zxing/ \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/01.png b/test_resources/blackbox/cpp/qrcode-5/01.png new file mode 100644 index 0000000..746d771 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/01.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/01.txt b/test_resources/blackbox/cpp/qrcode-5/01.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/01.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/02.png b/test_resources/blackbox/cpp/qrcode-5/02.png new file mode 100644 index 0000000..87604d0 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/02.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/02.txt b/test_resources/blackbox/cpp/qrcode-5/02.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/02.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/03.png b/test_resources/blackbox/cpp/qrcode-5/03.png new file mode 100644 index 0000000..af12df6 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/03.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/03.txt b/test_resources/blackbox/cpp/qrcode-5/03.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/03.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/04.png b/test_resources/blackbox/cpp/qrcode-5/04.png new file mode 100644 index 0000000..5e0f5d1 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/04.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/04.txt b/test_resources/blackbox/cpp/qrcode-5/04.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/04.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/05.png b/test_resources/blackbox/cpp/qrcode-5/05.png new file mode 100644 index 0000000..fc727c3 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/05.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/05.txt b/test_resources/blackbox/cpp/qrcode-5/05.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/05.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/08.png b/test_resources/blackbox/cpp/qrcode-5/08.png new file mode 100644 index 0000000..3fe39e2 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/08.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/08.txt b/test_resources/blackbox/cpp/qrcode-5/08.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/08.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/09.png b/test_resources/blackbox/cpp/qrcode-5/09.png new file mode 100644 index 0000000..3a19fa7 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/09.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/09.txt b/test_resources/blackbox/cpp/qrcode-5/09.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/09.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/11.png b/test_resources/blackbox/cpp/qrcode-5/11.png new file mode 100644 index 0000000..5efcd19 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/11.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/11.txt b/test_resources/blackbox/cpp/qrcode-5/11.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/11.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/12.png b/test_resources/blackbox/cpp/qrcode-5/12.png new file mode 100644 index 0000000..49b8a6f Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/12.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/12.txt b/test_resources/blackbox/cpp/qrcode-5/12.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/12.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/13.png b/test_resources/blackbox/cpp/qrcode-5/13.png new file mode 100644 index 0000000..fabf571 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/13.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/13.txt b/test_resources/blackbox/cpp/qrcode-5/13.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/13.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/14.png b/test_resources/blackbox/cpp/qrcode-5/14.png new file mode 100644 index 0000000..3c90b46 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/14.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/14.txt b/test_resources/blackbox/cpp/qrcode-5/14.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/14.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/15.png b/test_resources/blackbox/cpp/qrcode-5/15.png new file mode 100644 index 0000000..b472317 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/15.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/15.txt b/test_resources/blackbox/cpp/qrcode-5/15.txt new file mode 100644 index 0000000..424f852 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/15.txt @@ -0,0 +1 @@ +MECARD:N:Sean Owen;TEL:+12125658770;EMAIL:srowen@google.com;; \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-5/16.png b/test_resources/blackbox/cpp/qrcode-5/16.png new file mode 100644 index 0000000..fb27dac Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/16.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/16.txt b/test_resources/blackbox/cpp/qrcode-5/16.txt new file mode 100644 index 0000000..7175d7a --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/16.txt @@ -0,0 +1,57 @@ +THROUGH THE LOOKING-GLASS + +By Lewis Carroll + + +CHAPTER I. Looking-Glass house + +One thing was certain, that the WHITE kitten had had nothing to do with +it:--it was the black kitten's fault entirely. For the white kitten had +been having its face washed by the old cat for the last quarter of +an hour (and bearing it pretty well, considering); so you see that it +COULDN'T have had any hand in the mischief. + +The way Dinah washed her children's faces was this: first she held the +poor thing down by its ear with one paw, and then with the other paw she +rubbed its face all over, the wrong way, beginning at the nose: and +just now, as I said, she was hard at work on the white kitten, which was +lying quite still and trying to purr--no doubt feeling that it was all +meant for its good. + +But the black kitten had been finished with earlier in the afternoon, +and so, while Alice was sitting curled up in a corner of the great +arm-chair, half talking to herself and half asleep, the kitten had been +having a grand game of romps with the ball of worsted Alice had been +trying to wind up, and had been rolling it up and down till it had all +come undone again; and there it was, spread over the hearth-rug, all +knots and tangles, with the kitten running after its own tail in the +middle. + +'Oh, you wicked little thing!' cried Alice, catching up the kitten, and +giving it a little kiss to make it understand that it was in disgrace. +'Really, Dinah ought to have taught you better manners! You OUGHT, +Dinah, you know you ought!' she added, looking reproachfully at the old +cat, and speaking in as cross a voice as she could manage--and then she +scrambled back into the arm-chair, taking the kitten and the worsted +with her, and began winding up the ball again. But she didn't get on +very fast, as she was talking all the time, sometimes to the kitten, and +sometimes to herself. Kitty sat very demurely on her knee, pretending to +watch the progress of the winding, and now and then putting out one +paw and gently touching the ball, as if it would be glad to help, if it +might. + +'Do you know what to-morrow is, Kitty?' Alice began. 'You'd have guessed +if you'd been up in the window with me--only Dinah was making you tidy, +so you couldn't. I was watching the boys getting in sticks for the +bonfire--and it wants plenty of sticks, Kitty! Only it got so cold, and +it snowed so, they had to leave off. Never mind, Kitty, we'll go and +see the bonfire to-morrow.' Here Alice wound two or three turns of the +worsted round the kitten's neck, just to see how it would look: this led +to a scramble, in which the ball rolled down upon the floor, and yards +and yards of it got unwound again. + +'Do you know, I was so angry, Kitty,' Alice went on as soon as they were +comfortably settled again, 'when I saw all the mischief you had been +doing, I was very nearly opening the window, and putting you out into +the snow! And you'd have deserved it, you little mischievous darling! +Wha diff --git a/test_resources/blackbox/cpp/qrcode-5/17.png b/test_resources/blackbox/cpp/qrcode-5/17.png new file mode 100644 index 0000000..e30429a Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/17.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/17.txt b/test_resources/blackbox/cpp/qrcode-5/17.txt new file mode 100644 index 0000000..967a10b --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/17.txt @@ -0,0 +1,46 @@ +THROUGH THE LOOKING-GLASS + +By Lewis Carroll + + +CHAPTER I. Looking-Glass house + +One thing was certain, that the WHITE kitten had had nothing to do with +it:--it was the black kitten's fault entirely. For the white kitten had +been having its face washed by the old cat for the last quarter of +an hour (and bearing it pretty well, considering); so you see that it +COULDN'T have had any hand in the mischief. + +The way Dinah washed her children's faces was this: first she held the +poor thing down by its ear with one paw, and then with the other paw she +rubbed its face all over, the wrong way, beginning at the nose: and +just now, as I said, she was hard at work on the white kitten, which was +lying quite still and trying to purr--no doubt feeling that it was all +meant for its good. + +But the black kitten had been finished with earlier in the afternoon, +and so, while Alice was sitting curled up in a corner of the great +arm-chair, half talking to herself and half asleep, the kitten had been +having a grand game of romps with the ball of worsted Alice had been +trying to wind up, and had been rolling it up and down till it had all +come undone again; and there it was, spread over the hearth-rug, all +knots and tangles, with the kitten running after its own tail in the +middle. + +'Oh, you wicked little thing!' cried Alice, catching up the kitten, and +giving it a little kiss to make it understand that it was in disgrace. +'Really, Dinah ought to have taught you better manners! You OUGHT, +Dinah, you know you ought!' she added, looking reproachfully at the old +cat, and speaking in as cross a voice as she could manage--and then she +scrambled back into the arm-chair, taking the kitten and the worsted +with her, and began winding up the ball again. But she didn't get on +very fast, as she was talking all the time, sometimes to the kitten, and +sometimes to herself. Kitty sat very demurely on her knee, pretending to +watch the progress of the winding, and now and then putting out one +paw and gently touching the ball, as if it would be glad to help, if it +might. + +'Do you know what to-morrow is, Kitty?' Alice began. 'You'd have guessed +if you'd been up in the window with me--only Dinah was making you tidy, +so you couldn't. I was watching the boys getting in sticks for the +bonfire--and it wants plenty of sticks, Kitty! Only it diff --git a/test_resources/blackbox/cpp/qrcode-5/18.png b/test_resources/blackbox/cpp/qrcode-5/18.png new file mode 100644 index 0000000..be9943a Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/18.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/18.txt b/test_resources/blackbox/cpp/qrcode-5/18.txt new file mode 100644 index 0000000..863b77f --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/18.txt @@ -0,0 +1,36 @@ +THROUGH THE LOOKING-GLASS + +By Lewis Carroll + + +CHAPTER I. Looking-Glass house + +One thing was certain, that the WHITE kitten had had nothing to do with +it:--it was the black kitten's fault entirely. For the white kitten had +been having its face washed by the old cat for the last quarter of +an hour (and bearing it pretty well, considering); so you see that it +COULDN'T have had any hand in the mischief. + +The way Dinah washed her children's faces was this: first she held the +poor thing down by its ear with one paw, and then with the other paw she +rubbed its face all over, the wrong way, beginning at the nose: and +just now, as I said, she was hard at work on the white kitten, which was +lying quite still and trying to purr--no doubt feeling that it was all +meant for its good. + +But the black kitten had been finished with earlier in the afternoon, +and so, while Alice was sitting curled up in a corner of the great +arm-chair, half talking to herself and half asleep, the kitten had been +having a grand game of romps with the ball of worsted Alice had been +trying to wind up, and had been rolling it up and down till it had all +come undone again; and there it was, spread over the hearth-rug, all +knots and tangles, with the kitten running after its own tail in the +middle. + +'Oh, you wicked little thing!' cried Alice, catching up the kitten, and +giving it a little kiss to make it understand that it was in disgrace. +'Really, Dinah ought to have taught you better manners! You OUGHT, +Dinah, you know you ought!' she added, looking reproachfully at the old +cat, and speaking in as cross a voice as she could manage--and then she +scrambled back into the arm-ch + diff --git a/test_resources/blackbox/cpp/qrcode-5/19.png b/test_resources/blackbox/cpp/qrcode-5/19.png new file mode 100644 index 0000000..545d2ed Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-5/19.png differ diff --git a/test_resources/blackbox/cpp/qrcode-5/19.txt b/test_resources/blackbox/cpp/qrcode-5/19.txt new file mode 100644 index 0000000..845cd05 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-5/19.txt @@ -0,0 +1,28 @@ +THROUGH THE LOOKING-GLASS + +By Lewis Carroll + + +CHAPTER I. Looking-Glass house + +One thing was certain, that the WHITE kitten had had nothing to do with +it:--it was the black kitten's fault entirely. For the white kitten had +been having its face washed by the old cat for the last quarter of +an hour (and bearing it pretty well, considering); so you see that it +COULDN'T have had any hand in the mischief. + +The way Dinah washed her children's faces was this: first she held the +poor thing down by its ear with one paw, and then with the other paw she +rubbed its face all over, the wrong way, beginning at the nose: and +just now, as I said, she was hard at work on the white kitten, which was +lying quite still and trying to purr--no doubt feeling that it was all +meant for its good. + +But the black kitten had been finished with earlier in the afternoon, +and so, while Alice was sitting curled up in a corner of the great +arm-chair, half talking to herself and half asleep, the kitten had been +having a grand game of romps with the ball of worsted Alice had been +trying to wind up, and had been rolling it up and down till it had all +come undone again; and there it was, spread over the hearth-rug, all +knots and tangles, with the kitten running after its own tail in the +midd diff --git a/test_resources/blackbox/cpp/qrcode-6/1.png b/test_resources/blackbox/cpp/qrcode-6/1.png new file mode 100644 index 0000000..9c54da9 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/1.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/1.txt b/test_resources/blackbox/cpp/qrcode-6/1.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/1.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/10.png b/test_resources/blackbox/cpp/qrcode-6/10.png new file mode 100644 index 0000000..f603ab1 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/10.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/10.txt b/test_resources/blackbox/cpp/qrcode-6/10.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/10.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/11.png b/test_resources/blackbox/cpp/qrcode-6/11.png new file mode 100644 index 0000000..1ade6f5 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/11.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/11.txt b/test_resources/blackbox/cpp/qrcode-6/11.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/11.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/12.png b/test_resources/blackbox/cpp/qrcode-6/12.png new file mode 100644 index 0000000..bd03f12 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/12.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/12.txt b/test_resources/blackbox/cpp/qrcode-6/12.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/12.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/13.png b/test_resources/blackbox/cpp/qrcode-6/13.png new file mode 100644 index 0000000..ddecef9 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/13.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/13.txt b/test_resources/blackbox/cpp/qrcode-6/13.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/13.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/14.png b/test_resources/blackbox/cpp/qrcode-6/14.png new file mode 100644 index 0000000..7cf8b68 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/14.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/14.txt b/test_resources/blackbox/cpp/qrcode-6/14.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/14.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/15.png b/test_resources/blackbox/cpp/qrcode-6/15.png new file mode 100644 index 0000000..a22aa94 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/15.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/15.txt b/test_resources/blackbox/cpp/qrcode-6/15.txt new file mode 100644 index 0000000..3b12464 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/15.txt @@ -0,0 +1 @@ +TEST \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/2.png b/test_resources/blackbox/cpp/qrcode-6/2.png new file mode 100644 index 0000000..cc4b119 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/2.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/2.txt b/test_resources/blackbox/cpp/qrcode-6/2.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/2.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/3.png b/test_resources/blackbox/cpp/qrcode-6/3.png new file mode 100644 index 0000000..0112d17 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/3.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/3.txt b/test_resources/blackbox/cpp/qrcode-6/3.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/3.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/4.png b/test_resources/blackbox/cpp/qrcode-6/4.png new file mode 100644 index 0000000..a88b8dc Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/4.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/4.txt b/test_resources/blackbox/cpp/qrcode-6/4.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/4.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/5.png b/test_resources/blackbox/cpp/qrcode-6/5.png new file mode 100644 index 0000000..0e41c2c Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/5.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/5.txt b/test_resources/blackbox/cpp/qrcode-6/5.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/5.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/6.png b/test_resources/blackbox/cpp/qrcode-6/6.png new file mode 100644 index 0000000..0d382a8 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/6.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/6.txt b/test_resources/blackbox/cpp/qrcode-6/6.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/6.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/7.png b/test_resources/blackbox/cpp/qrcode-6/7.png new file mode 100644 index 0000000..340a3ca Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/7.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/7.txt b/test_resources/blackbox/cpp/qrcode-6/7.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/7.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/8.png b/test_resources/blackbox/cpp/qrcode-6/8.png new file mode 100644 index 0000000..d0d9bcc Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/8.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/8.txt b/test_resources/blackbox/cpp/qrcode-6/8.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/8.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-6/9.png b/test_resources/blackbox/cpp/qrcode-6/9.png new file mode 100644 index 0000000..43070ff Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-6/9.png differ diff --git a/test_resources/blackbox/cpp/qrcode-6/9.txt b/test_resources/blackbox/cpp/qrcode-6/9.txt new file mode 100644 index 0000000..6a537b5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-6/9.txt @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/qrcode-7/01-01.png b/test_resources/blackbox/cpp/qrcode-7/01-01.png new file mode 100644 index 0000000..3358f3d Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-7/01-01.png differ diff --git a/test_resources/blackbox/cpp/qrcode-7/01-02.png b/test_resources/blackbox/cpp/qrcode-7/01-02.png new file mode 100644 index 0000000..343316b Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-7/01-02.png differ diff --git a/test_resources/blackbox/cpp/qrcode-7/01-03.png b/test_resources/blackbox/cpp/qrcode-7/01-03.png new file mode 100644 index 0000000..7fff534 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-7/01-03.png differ diff --git a/test_resources/blackbox/cpp/qrcode-7/01-04.png b/test_resources/blackbox/cpp/qrcode-7/01-04.png new file mode 100644 index 0000000..348fc23 Binary files /dev/null and b/test_resources/blackbox/cpp/qrcode-7/01-04.png differ diff --git a/test_resources/blackbox/cpp/qrcode-7/01.metadata.txt b/test_resources/blackbox/cpp/qrcode-7/01.metadata.txt new file mode 100644 index 0000000..0631ce5 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-7/01.metadata.txt @@ -0,0 +1,6 @@ +symbologyIdentifier=]Q1 +# ecLevel not set when "runStructuredAppendTest" +sequenceSize=4 +# sequenceIndex set to -1 in MergeStructuredAppendResults +sequenceIndex=-1 +sequenceId=95 diff --git a/test_resources/blackbox/cpp/qrcode-7/01.txt b/test_resources/blackbox/cpp/qrcode-7/01.txt new file mode 100644 index 0000000..8ac6a93 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-7/01.txt @@ -0,0 +1,49 @@ +initrd vs initramfs + +Monolithic vs Modular kernel vs MicroKernel + +Monolithic: + All drivers, operations and code to perform kernel related tasks are compilied into the kernel. Interactions with kernel are done through system calls. + advantages: + 1) Less code = less complexity + 2) Less code = smaller size + 3) Less code = fewer bugs and security issues + disadvantages + 1) difficult to patch and test + 2) bugs can affect large parts of system + +Microkernel + Only the fundamental tasks and drives are handled by the kernel - memory management, passing messages between processes. + All other tasks are handled in user space by "servers" or "user-mode servers." + advantages + 1) easier to maintain + 2) testing is easier - can swap patches in and out + disadvantages + 1) Larger running footprint + 2) More complex to interact with - port calls from user-mode servers + 3) Process management can be more complex + +Modular Kernel + A monolithic kernel with binary modules/drivers that can me dynamically loaded or unloaded. + advantages: + 1) Faster and easier development for drivers that can operate as modules + 2) Easier to added drivers/services via modules + disadvantages + 1) More interfaces to pass through increases possibility of bugs + 2) Maintaining modules can be more difficult + + +To be clear, loading a module dynamically into the kernel can incure some overhead compared to having the module compiled into the kernel. But, +this can be offset by having an overall smaller footprint in the monolithic kernel due to not having unnecessary code compiled into the kernel. This +is chiefly beneficial to embedded systems. + + + + + +Resources + +http://www.systhread.net/texts/200510kdiff.php +http://en.wikipedia.org/wiki/Microkernel +http://en.wikipedia.org/wiki/Monolithic_kernel +http://en.wikipedia.org/wiki/Initramfs diff --git a/tests/common/abstract_black_box_test_case.rs b/tests/common/abstract_black_box_test_case.rs index b0f7d1b..26cbb08 100644 --- a/tests/common/abstract_black_box_test_case.rs +++ b/tests/common/abstract_black_box_test_case.rs @@ -46,6 +46,7 @@ pub struct AbstractBlackBoxTestCase { expected_format: BarcodeFormat, test_rxing_results: Vec, hints: HashMap, + pub ignore_pure: bool, } impl AbstractBlackBoxTestCase { @@ -70,6 +71,7 @@ impl AbstractBlackBoxTestCase { expected_format, test_rxing_results: Vec::new(), hints: HashMap::new(), + ignore_pure: false, } } @@ -242,6 +244,9 @@ impl AbstractBlackBoxTestCase { RXingResultMetadataType::CONTENT_TYPE => { RXingResultMetadataValue::ContentType(v) } + RXingResultMetadataType::IS_INVERTED => { + RXingResultMetadataValue::IsInverted(v.parse().unwrap()) + } }; expected_metadata.insert(new_k, new_v); } @@ -447,20 +452,25 @@ impl AbstractBlackBoxTestCase { // hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); } - // Try in 'pure' mode mostly to exercise PURE_BARCODE code paths for exceptions; - // not expected to pass, generally + let mut result = None; + + if !self.ignore_pure { + // Try in 'pure' mode mostly to exercise PURE_BARCODE code paths for exceptions; + // not expected to pass, generally + let mut pure_hints = HashMap::new(); + pure_hints.insert( + DecodeHintType::PURE_BARCODE, + DecodeHintValue::PureBarcode(true), + ); + + result = if let Ok(res) = self.barcode_reader.decode_with_hints(source, &pure_hints) { + Some(res) + } else { + None + }; + } + // let mut result = None; - let mut pure_hints = HashMap::new(); - pure_hints.insert( - DecodeHintType::PURE_BARCODE, - DecodeHintValue::PureBarcode(true), - ); - let mut result = if let Ok(res) = self.barcode_reader.decode_with_hints(source, &pure_hints) - { - Some(res) - } else { - None - }; if result.is_none() { result = Some(self.barcode_reader.decode_with_hints(source, &hints)?) diff --git a/tests/common/mod.rs b/tests/common/mod.rs index a9d2073..4c2bc5f 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,7 +1,7 @@ mod abstract_black_box_test_case; -mod pdf_417_multiimage_span; +mod multiimage_span; mod test_result; pub use abstract_black_box_test_case::*; -pub use pdf_417_multiimage_span::*; +pub use multiimage_span::*; pub use test_result::*; diff --git a/tests/common/pdf_417_multiimage_span.rs b/tests/common/multiimage_span.rs similarity index 94% rename from tests/common/pdf_417_multiimage_span.rs rename to tests/common/multiimage_span.rs index d7a3bf3..43b048a 100644 --- a/tests/common/pdf_417_multiimage_span.rs +++ b/tests/common/multiimage_span.rs @@ -37,7 +37,7 @@ use super::TestRXingResult; * @author Sean Owen * @author dswitkin@google.com (Daniel Switkin) */ -pub struct PDF417MultiImageSpanAbstractBlackBoxTestCase { +pub struct MultiImageSpanAbstractBlackBoxTestCase { test_base: Box, barcode_reader: T, expected_format: BarcodeFormat, @@ -45,7 +45,7 @@ pub struct PDF417MultiImageSpanAbstractBlackBoxTestCase, } -impl PDF417MultiImageSpanAbstractBlackBoxTestCase { +impl MultiImageSpanAbstractBlackBoxTestCase { pub fn build_test_base(test_base_path_suffix: &str) -> Box { // A little workaround to prevent aggravation in my IDE let test_base = Path::new(test_base_path_suffix); @@ -198,19 +198,36 @@ impl PDF417MultiImageSpanAbstractBlackBoxTest // results.sort(); let mut result_text = String::new(); //new StringBuilder(); let mut file_id: Option = None; - for result in results { - // for (RXingResult result : results) { - let result_metadata = Self::get_meta(&result); - assert!(result_metadata.is_some(), "resultMetadata"); - if file_id.is_none() { - file_id = Some(result_metadata.as_ref().unwrap().getFileId().to_owned()); + if self.expected_format == BarcodeFormat::PDF_417 { + for result in results { + // for (RXingResult result : results) { + let result_metadata = Self::get_meta(&result); + assert!(result_metadata.is_some(), "resultMetadata"); + if file_id.is_none() { + file_id = + Some(result_metadata.as_ref().unwrap().getFileId().to_owned()); + } + assert_eq!( + file_id, + Some(result_metadata.as_ref().unwrap().getFileId().to_owned()), + "FileId" + ); + result_text.push_str(result.getText()); + } + } else if self.expected_format != BarcodeFormat::PDF_417 { + results.sort_by_key(|r| { + if let Some(RXingResultMetadataValue::StructuredAppendSequence(md)) = r + .getRXingResultMetadata() + .get(&RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE) + { + *md + } else { + 0 + } + }); + for result in results { + result_text.push_str(result.getText()); } - assert_eq!( - file_id, - Some(result_metadata.as_ref().unwrap().getFileId().to_owned()), - "FileId" - ); - result_text.push_str(result.getText()); } assert_eq!(expected_text, result_text, "ExpectedText"); passed_counts[x] += 1; @@ -384,6 +401,9 @@ impl PDF417MultiImageSpanAbstractBlackBoxTest RXingResultMetadataType::CONTENT_TYPE => { RXingResultMetadataValue::ContentType(v) } + RXingResultMetadataType::IS_INVERTED => { + RXingResultMetadataValue::IsInverted(v.parse().unwrap()) + } }; expected_metadata.insert(new_k, new_v); } diff --git a/tests/cpp_qr_code_blackbox_tests.rs b/tests/cpp_qr_code_blackbox_tests.rs new file mode 100644 index 0000000..3964b7c --- /dev/null +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -0,0 +1,313 @@ +/* + * 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. + */ + +use rxing::{qrcode::cpp_port::QrReader, BarcodeFormat, MultiUseMultiFormatReader}; + +mod common; + +/** + * @author Sean Owen + */ + +#[test] +fn qrcode_black_box1_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/qrcode-1", + QrReader::default(), + rxing::BarcodeFormat::QR_CODE, + ); + // super("src/test/resources/blackbox/qrcode-1", new MultiFormatReader(), BarcodeFormat.QR_CODE); + tester.add_test(17, 17, 0.0); + tester.add_test(14, 14, 90.0); + tester.add_test(17, 17, 180.0); + tester.add_test(16, 16, 270.0); + + tester.test_black_box(); +} + +/** + * @author Sean Owen + */ + +// #[test] +// fn qrcode_black_box2_test_case() { +// let mut tester = common::AbstractBlackBoxTestCase::new( +// "test_resources/blackbox/qrcode-2", +// // MultiFormatReader::default(), +// QrReader {}, +// BarcodeFormat::QR_CODE, +// ); +// tester.add_test(31, 31, 0.0); +// tester.add_test(29, 29, 90.0); +// tester.add_test(30, 30, 180.0); +// tester.add_test(30, 30, 270.0); + +// tester.ignore_pure = false; + +// tester.test_black_box(); +// } + +/** + * @author dswitkin@google.com (Daniel Switkin) + */ + +#[test] +fn qrcode_black_box3_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/qrcode-3", + QrReader::default(), + BarcodeFormat::QR_CODE, + ); + tester.add_test(38, 38, 0.0); + tester.add_test(39, 39, 90.0); + tester.add_test(36, 36, 180.0); + tester.add_test(39, 39, 270.0); + + tester.test_black_box(); +} + +/** + * Tests of various QR Codes from t-shirts, which are notoriously not flat. + * + * @author dswitkin@google.com (Daniel Switkin) + */ + +#[test] +fn qrcode_black_box4_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/qrcode-4", + QrReader::default(), + // QRCodeReader::new(), + BarcodeFormat::QR_CODE, + ); + tester.add_test(36, 36, 0.0); + tester.add_test(35, 35, 90.0); + tester.add_test(35, 35, 180.0); + tester.add_test(35, 35, 270.0); + + tester.test_black_box(); +} + +/** + * Some very difficult exposure conditions including self-shadowing, which happens a lot when + * pointing down at a barcode (i.e. the phone's shadow falls across part of the image). + * The global histogram gets about 5/15, where the local one gets 15/15. + * + * @author dswitkin@google.com (Daniel Switkin) + */ + +#[test] +fn qrcode_black_box5_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/qrcode-5", + QrReader::default(), + BarcodeFormat::QR_CODE, + ); + tester.add_test(16, 16, 0.0); + tester.add_test(16, 16, 90.0); + tester.add_test(16, 16, 180.0); + tester.add_test(16, 16, 270.0); + + tester.test_black_box(); +} + +/** + * These tests are supplied by Tim Gernat and test finder pattern detection at small size and under + * rotation, which was a weak spot. + */ + +#[test] +fn qrcode_black_box6_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/qrcode-6", + QrReader::default(), + BarcodeFormat::QR_CODE, + ); + tester.add_test(15, 15, 0.0); + tester.add_test(14, 14, 90.0); + tester.add_test(13, 13, 180.0); + tester.add_test(14, 14, 270.0); + + tester.test_black_box(); +} + +#[test] +fn mqr_black_box_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/cpp/microqrcode-1", + QrReader::default(), + BarcodeFormat::MICRO_QR_CODE, + ); + + tester.add_test(15, 15, 0.0); + tester.add_test(15, 15, 90.0); + tester.add_test(15, 13, 180.0); + tester.add_test(15, 15, 270.0); + + tester.test_black_box(); +} + +/** + * @author Sean Owen + * + * 16.png seems to be an odd case where in both c++ and rs the result of a pure read + * is different than the result of a detect. there is an extra ' ' in the output of + * the pure read. I'm not sure how to fix this, as it occurs in both and seems to just + * be a function of the barcode. + */ + +#[test] +fn cpp_qrcode_black_box1_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/cpp/qrcode-1", + QrReader::default(), + rxing::BarcodeFormat::QR_CODE, + ); + // super("src/test/resources/blackbox/qrcode-1", new MultiFormatReader(), BarcodeFormat.QR_CODE); + tester.add_test(16, 16, 0.0); + tester.add_test(16, 16, 90.0); + tester.add_test(16, 16, 180.0); + tester.add_test(16, 16, 270.0); + + tester.test_black_box(); +} + +/** + * @author Sean Owen + */ + +#[test] +fn cpp_qrcode_black_box2_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/cpp/qrcode-2", + MultiUseMultiFormatReader::default(), + // QrReader::default(), + BarcodeFormat::QR_CODE, + ); + + tester.add_test(45, 47, 0.0); + tester.add_test(45, 47, 90.0); + tester.add_test(45, 47, 180.0); + tester.add_test(45, 46, 270.0); + + tester.ignore_pure = true; + + tester.add_hint( + rxing::DecodeHintType::ALSO_INVERTED, + rxing::DecodeHintValue::AlsoInverted(true), + ); + + tester.test_black_box(); +} + +/** + * @author dswitkin@google.com (Daniel Switkin) + */ + +#[test] +fn cpp_qrcode_black_box3_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/cpp/qrcode-3", + QrReader::default(), + BarcodeFormat::QR_CODE, + ); + + tester.add_test(28, 28, 0.0); + tester.add_test(28, 28, 90.0); + tester.add_test(28, 28, 180.0); + tester.add_test(27, 27, 270.0); + + tester.test_black_box(); +} + +/** + * Tests of various QR Codes from t-shirts, which are notoriously not flat. + * + * @author dswitkin@google.com (Daniel Switkin) + */ + +#[test] +fn cpp_qrcode_black_box4_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/cpp/qrcode-4", + QrReader::default(), + // QRCodeReader::new(), + BarcodeFormat::QR_CODE, + ); + tester.add_test(29, 29, 0.0); + tester.add_test(29, 29, 90.0); + tester.add_test(29, 29, 180.0); + tester.add_test(29, 29, 270.0); + + tester.test_black_box(); +} + +/** + * Some very difficult exposure conditions including self-shadowing, which happens a lot when + * pointing down at a barcode (i.e. the phone's shadow falls across part of the image). + * The global histogram gets about 5/15, where the local one gets 15/15. + * + * @author dswitkin@google.com (Daniel Switkin) + */ + +#[test] +fn cpp_qrcode_black_box5_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/cpp/qrcode-5", + QrReader::default(), + BarcodeFormat::QR_CODE, + ); + tester.add_test(16, 16, 0.0); + tester.add_test(16, 16, 90.0); + tester.add_test(16, 16, 180.0); + tester.add_test(16, 16, 270.0); + + tester.test_black_box(); +} + +/** + * These tests are supplied by Tim Gernat and test finder pattern detection at small size and under + * rotation, which was a weak spot. + */ + +#[test] +fn cpp_qrcode_black_box6_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/cpp/qrcode-6", + QrReader::default(), + BarcodeFormat::QR_CODE, + ); + tester.add_test(15, 15, 0.0); + tester.add_test(15, 15, 90.0); + tester.add_test(15, 15, 180.0); + tester.add_test(15, 15, 270.0); + + tester.test_black_box(); +} + +#[test] +fn cpp_qrcode_black_box7_test_case() { + let mut tester = common::MultiImageSpanAbstractBlackBoxTestCase::new( + "test_resources/blackbox/cpp/qrcode-7", + QrReader::default(), + rxing::BarcodeFormat::QR_CODE, + ); + + // super("src/test/resources/blackbox/pdf417-4", null, BarcodeFormat.PDF_417); + tester.add_test_complex(1, 1, 0, 0, 0.0); + + tester.test_black_box(); +} diff --git a/tests/cropped_transposed_luma.rs b/tests/cropped_transposed_luma.rs index f5747e2..af2382d 100644 --- a/tests/cropped_transposed_luma.rs +++ b/tests/cropped_transposed_luma.rs @@ -3,7 +3,7 @@ fn test_binarizer_init_empty() { assert!(rxing::helpers::detect_multiple_in_luma(DATA.to_vec(), 665, 286).is_ok()) } -static DATA: [u8; 190190] = [ +const DATA: [u8; 190190] = [ 220, 224, 216, 219, 221, 223, 221, 217, 216, 212, 203, 207, 213, 214, 208, 209, 218, 223, 221, 218, 212, 210, 202, 204, 209, 203, 211, 212, 216, 220, 223, 223, 221, 220, 219, 219, 219, 217, 214, 213, 215, 215, 213, 214, 214, 209, 203, 204, 212, 219, 221, 220, 225, 221, 208, 202, 205, diff --git a/tests/pdf_417_black_box_4_testcase.rs b/tests/pdf_417_black_box_4_testcase.rs index 3805379..ed45c44 100644 --- a/tests/pdf_417_black_box_4_testcase.rs +++ b/tests/pdf_417_black_box_4_testcase.rs @@ -25,7 +25,7 @@ use rxing::pdf417::PDF417Reader; */ #[test] fn pdf417_black_box4_test_case() { - let mut tester = common::PDF417MultiImageSpanAbstractBlackBoxTestCase::new( + let mut tester = common::MultiImageSpanAbstractBlackBoxTestCase::new( "test_resources/blackbox/pdf417-4", PDF417Reader::default(), rxing::BarcodeFormat::PDF_417,