diff --git a/Cargo.lock b/Cargo.lock index 1f6dbd5..f984048 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1645,7 +1645,7 @@ dependencies = [ "gpio", "gui-server-api", "i2c", - "image 0.25.6", + "image 0.25.8", "log 0.4.27", "nokhwa", "num-derive", @@ -3369,6 +3369,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "endi" version = "1.1.0" @@ -3528,7 +3537,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" dependencies = [ "bit-set", - "regex-automata 0.4.9", + "regex-automata 0.4.14", + "regex-syntax", +] + +[[package]] +name = "fancy-regex" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8" +dependencies = [ + "bit-set", + "regex-automata 0.4.14", "regex-syntax", ] @@ -3541,7 +3561,7 @@ dependencies = [ "bytemuck", "cfg-if 1.0.0", "document-features", - "image 0.25.6", + "image 0.25.8", "num-traits", "thiserror 1.0.64", ] @@ -3565,6 +3585,26 @@ dependencies = [ "log 0.4.27", ] +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "fdeflate" version = "0.3.4" @@ -3680,9 +3720,9 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -4227,7 +4267,7 @@ dependencies = [ "aho-corasick", "bstr", "log 0.4.27", - "regex-automata 0.4.9", + "regex-automata 0.4.14", "regex-syntax", ] @@ -4509,7 +4549,7 @@ name = "gui-app-image-viewer" version = "0.1.0" dependencies = [ "fs", - "image 0.25.6", + "image 0.25.8", "log 0.4.27", "slint-keyos-platform", "slint-keyos-platform-build", @@ -4524,7 +4564,7 @@ dependencies = [ "fs", "gui-server-api", "haptics", - "image 0.25.6", + "image 0.25.8", "libblur", "log 0.4.27", "num-traits", @@ -4640,8 +4680,8 @@ dependencies = [ "haptics", "i18n", "log 0.4.27", - "quircs", "rgb565", + "rxing 0.8.5", "server", "settings", "slint-keyos-platform", @@ -4845,7 +4885,7 @@ dependencies = [ "gui-server-api", "haptics", "i2c", - "image 0.25.6", + "image 0.25.8", "image_swizzle", "libblur", "log 0.4.27", @@ -5217,7 +5257,7 @@ dependencies = [ "derive_more 2.0.1", "fontdue", "i-slint-common", - "image 0.25.6", + "image 0.25.8", "itertools 0.14.0", "linked_hash_set", "lyon_extra", @@ -5252,7 +5292,7 @@ dependencies = [ "euclid", "i-slint-common", "i-slint-core-macros", - "image 0.25.6", + "image 0.25.8", "integer-sqrt", "lyon_algorithms", "lyon_extra", @@ -5837,7 +5877,7 @@ dependencies = [ "globset", "log 0.4.27", "memchr", - "regex-automata 0.4.9", + "regex-automata 0.4.14", "same-file", "walkdir", "winapi-util", @@ -5856,16 +5896,16 @@ dependencies = [ "gif", "jpeg-decoder", "num-traits", - "png", + "png 0.17.13", "qoi", - "tiff", + "tiff 0.9.1", ] [[package]] name = "image" -version = "0.25.6" +version = "0.25.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" dependencies = [ "bytemuck", "byteorder-lite", @@ -5873,13 +5913,14 @@ dependencies = [ "exr", "gif", "image-webp", + "moxcms", "num-traits", - "png", + "png 0.18.1", "qoi", "ravif", "rayon", "rgb", - "tiff", + "tiff 0.10.3", "zune-core", "zune-jpeg", ] @@ -5912,7 +5953,7 @@ dependencies = [ "ab_glyph", "approx", "getrandom 0.2.10", - "image 0.25.6", + "image 0.25.8", "itertools 0.12.1", "nalgebra", "num", @@ -5929,9 +5970,9 @@ checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" [[package]] name = "imgref" -version = "1.10.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" +checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" [[package]] name = "indexmap" @@ -6299,7 +6340,7 @@ dependencies = [ "keyos", "micro-ecc-sys", "png-decoder", - "rxing", + "rxing 0.7.1", "securam-manager", "sha2", ] @@ -7126,6 +7167,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -7140,6 +7182,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "moxcms" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "mozjpeg" version = "0.10.13" @@ -7179,7 +7231,7 @@ dependencies = [ "objc2-core-foundation", "objc2-foundation 0.3.1", "once_cell", - "png", + "png 0.17.13", "thiserror 2.0.11", "windows-sys 0.59.0", ] @@ -7482,7 +7534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c35ed9613f002f8095aafc97ad839e0bb6cebf79111c68265d8df212a5a294" dependencies = [ "flume", - "image 0.25.6", + "image 0.25.8", "nokhwa-bindings-linux", "nokhwa-bindings-macos", "nokhwa-bindings-windows", @@ -7536,7 +7588,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "903f3e0f406f7e9aad4fa0566c1d97cc7f88aab57847e1f919d1a34812dedee3" dependencies = [ "bytes", - "image 0.25.6", + "image 0.25.8", "mozjpeg", "thiserror 2.0.11", ] @@ -8582,6 +8634,19 @@ dependencies = [ "miniz_oxide 0.7.4", ] +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.6.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.0", +] + [[package]] name = "png-decoder" version = "0.1.1" @@ -8909,6 +8974,12 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "pxfm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" + [[package]] name = "qbsdiff" version = "1.4.4" @@ -9036,17 +9107,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "quircs" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3fec90fc7a119692a6244ed4404d152878a47f0bf3f90f34dc79bb7a339d24" -dependencies = [ - "num-derive", - "num-traits", - "thiserror 1.0.64", -] - [[package]] name = "quote" version = "1.0.40" @@ -9260,9 +9320,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.11" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" dependencies = [ "avif-serialize", "imgref", @@ -9387,13 +9447,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", + "regex-automata 0.4.14", "regex-syntax", ] @@ -9408,9 +9468,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -9858,14 +9918,36 @@ dependencies = [ "chrono-tz", "codepage-437", "encoding", - "fancy-regex", - "image 0.25.6", + "fancy-regex 0.14.0", + "image 0.25.8", "imageproc", "multimap", "num", "once_cell", "regex", - "rxing-one-d-proc-derive", + "rxing-one-d-proc-derive 0.6.0", + "thiserror 2.0.11", + "unicode-segmentation", + "uriparse", + "urlencoding", +] + +[[package]] +name = "rxing" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4fcbbc6ac324d7018be17d2c94c6c4690ca2f95b9345ffbc001fe8e459ebeb" +dependencies = [ + "chrono", + "chrono-tz", + "codepage-437", + "encoding_rs", + "fancy-regex 0.17.0", + "multimap", + "num", + "once_cell", + "regex", + "rxing-one-d-proc-derive 0.8.0", "thiserror 2.0.11", "unicode-segmentation", "uriparse", @@ -9882,6 +9964,16 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "rxing-one-d-proc-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7e5f136594d614d0d8e44f63508abd25c8fe0280655bb1d477b177bf718d0a4" +dependencies = [ + "quote", + "syn 2.0.100", +] + [[package]] name = "ryu" version = "1.0.18" @@ -10537,7 +10629,7 @@ version = "0.1.0" dependencies = [ "anyhow", "gui-server-api", - "image 0.25.6", + "image 0.25.8", "log 0.4.27", "serde", "serde_json", @@ -10650,7 +10742,7 @@ dependencies = [ "i-slint-common", "i-slint-core", "i18n", - "image 0.25.6", + "image 0.25.8", "indoc", "itertools 0.13.0", "itoa", @@ -10687,7 +10779,7 @@ dependencies = [ "anyhow", "euclid", "i-slint-compiler", - "image 0.25.6", + "image 0.25.8", "indoc", "itertools 0.13.0", "miette", @@ -11432,6 +11524,20 @@ dependencies = [ "weezl", ] +[[package]] +name = "tiff" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error 2.0.1", + "weezl", + "zune-jpeg", +] + [[package]] name = "time" version = "0.3.47" @@ -11475,7 +11581,7 @@ dependencies = [ "arrayvec", "bytemuck", "cfg-if 1.0.0", - "png", + "png 0.17.13", "tiny-skia-path 0.8.4", ] @@ -11490,7 +11596,7 @@ dependencies = [ "bytemuck", "cfg-if 1.0.0", "log 0.4.27", - "png", + "png 0.17.13", "tiny-skia-path 0.11.4", ] @@ -12737,9 +12843,9 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.8" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "whence" @@ -13949,9 +14055,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.14" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ "zune-core", ] diff --git a/apps/gui-app-qr-scanner/Cargo.toml b/apps/gui-app-qr-scanner/Cargo.toml index af8434f..2f198ad 100644 --- a/apps/gui-app-qr-scanner/Cargo.toml +++ b/apps/gui-app-qr-scanner/Cargo.toml @@ -14,8 +14,8 @@ haptics = { workspace = true } i18n = { path = "../../i18n" } log = { workspace = true } log-server = { workspace = true } -quircs = "0.10.2" rgb565 = "0.1.3" +rxing = { version = "0.8.5", default-features = false, features = ["encoding_rs"] } server = { workspace = true } settings = { workspace = true } slint-keyos-platform = { path = "../../slint-keyos-platform/runtime" } diff --git a/apps/gui-app-qr-scanner/src/main.rs b/apps/gui-app-qr-scanner/src/main.rs index f880508..ba84337 100644 --- a/apps/gui-app-qr-scanner/src/main.rs +++ b/apps/gui-app-qr-scanner/src/main.rs @@ -5,7 +5,10 @@ use { crate::settings_permissions::SettingsPermissions, foundation_ur::{bytewords, Decoder as UrDecoder}, log::debug, - quircs::Quirc, + rxing::{ + common::HybridBinarizer, BarcodeFormat, BinaryBitmap, DecodeHints, Luma8LuminanceSource, + MultiFormatReader, + }, slint_keyos_platform::{ app, gui_server_api::{ @@ -16,7 +19,7 @@ use { slint::{ComponentHandle, Timer, TimerMode}, spawn_local, subscribe_scalar, StoredValue, }, - std::{rc::Rc, thread, time::Duration}, + std::{rc::Rc, thread, time::Duration, collections::HashSet}, }; camera::use_api!(); @@ -41,100 +44,92 @@ enum ScanQrProgress { struct AppState { status: ScanStatus, - scanner: Quirc, - frame_luma8: [u8; CAMERA_WIDTH * CAMERA_HEIGHT], + scanner: MultiFormatReader, + luma_source: Luma8LuminanceSource, ur_decoder: UrDecoder, } impl AppState { fn scan_qr(&mut self) -> ScanQrProgress { - for code in self.scanner.identify(CAMERA_WIDTH, CAMERA_HEIGHT, &mut self.frame_luma8) { - let has_code = match code { - Ok(c) => c, - Err(e) => { - log::warn!("Extract error: {:?}", e); - return ScanQrProgress::Unchanged; + let Ok(has_code) = self + .scanner + .decode_with_state(&mut BinaryBitmap::new(HybridBinarizer::new(self.luma_source.clone()))) + .inspect_err(|e| { + if !matches!(e, rxing::Exceptions::NotFoundException(_)) { + log::warn!("Extract error: {:?}", e) } - }; - - let data = match has_code.decode() { - Ok(d) => d, - Err(e) => { - log::warn!("Decode error: {:?}", e); - return ScanQrProgress::Unchanged; - } - }; - - let data = data.payload.as_slice(); - - if let Ok(data_str) = std::str::from_utf8(data) { - match (self.ur_decoder.is_empty(), foundation_ur::UR::parse(data_str.to_lowercase().as_str())) - { - (true, Ok(part)) => { - let bw = match part.as_bytewords() { - Some(v) => v, - None => return ScanQrProgress::Unchanged, - }; - - if let Err(e) = bytewords::validate(bw, bytewords::Style::Minimal) { - log::warn!("Bytewords error: {:?}", e); - return ScanQrProgress::Unchanged; - }; - - if let Err(e) = self.ur_decoder.receive(part) { - log::warn!("Could not receive UR part: {}", e); - return ScanQrProgress::Unchanged; - } + }) + else { + return ScanQrProgress::Unchanged; + }; + + let data = has_code.getRawBytes().iter().as_slice(); + + if let Ok(data_str) = std::str::from_utf8(data) { + match (self.ur_decoder.is_empty(), foundation_ur::UR::parse(data_str.to_lowercase().as_str())) { + (true, Ok(part)) => { + let bw = match part.as_bytewords() { + Some(v) => v, + None => return ScanQrProgress::Unchanged, + }; + + if let Err(e) = bytewords::validate(bw, bytewords::Style::Minimal) { + log::warn!("Bytewords error: {:?}", e); + return ScanQrProgress::Unchanged; + }; + + if let Err(e) = self.ur_decoder.receive(part) { + log::warn!("Could not receive UR part: {}", e); + return ScanQrProgress::Unchanged; } - (false, Ok(part)) => { - if let Err(e) = self.ur_decoder.receive(part) { - log::warn!("Could not receive UR part: {}", e); - return ScanQrProgress::Unchanged; - } + } + (false, Ok(part)) => { + if let Err(e) = self.ur_decoder.receive(part) { + log::warn!("Could not receive UR part: {}", e); + return ScanQrProgress::Unchanged; } - (true, Err(_)) => (), - (false, Err(_)) => return ScanQrProgress::Unchanged, } + (true, Err(_)) => (), + (false, Err(_)) => return ScanQrProgress::Unchanged, } + } - if self.ur_decoder.is_complete() { - let ur_type = match self.ur_decoder.ur_type() { - Some(t) => t, - None => { - log::warn!("UR has no type"); - self.ur_decoder.clear(); - return ScanQrProgress::Progress(0.0); - } - }; - - let message_opt = match self.ur_decoder.message() { - Ok(m) => m, - Err(e) => { - log::warn!("UR Decoder state error: {}", e); - self.ur_decoder.clear(); - return ScanQrProgress::Progress(0.0); - } - }; + if self.ur_decoder.is_complete() { + let ur_type = match self.ur_decoder.ur_type() { + Some(t) => t, + None => { + log::warn!("UR has no type"); + self.ur_decoder.clear(); + return ScanQrProgress::Progress(0.0); + } + }; - let message = match message_opt { - Some(m) => m, - None => { - log::warn!("No message in UR code"); - self.ur_decoder.clear(); - return ScanQrProgress::Progress(0.0); - } - }; + let message_opt = match self.ur_decoder.message() { + Ok(m) => m, + Err(e) => { + log::warn!("UR Decoder state error: {}", e); + self.ur_decoder.clear(); + return ScanQrProgress::Progress(0.0); + } + }; + let message = match message_opt { + Some(m) => m, + None => { + log::warn!("No message in UR code"); + self.ur_decoder.clear(); + return ScanQrProgress::Progress(0.0); + } + }; - let res = ScanQrResult::new_ur2(String::from(ur_type), message); - self.ur_decoder.clear(); - return ScanQrProgress::Complete(res); - } + let res = ScanQrResult::new_ur2(String::from(ur_type), message); + self.ur_decoder.clear(); + return ScanQrProgress::Complete(res); + } - // Only reachable if the ur_decoder is empty - // and the data wasn't a valid UR frame - if self.ur_decoder.is_empty() { - return ScanQrProgress::Complete(ScanQrResult::new_qr(data)); - } + // Only reachable if the ur_decoder is empty + // and the data wasn't a valid UR frame + if self.ur_decoder.is_empty() { + return ScanQrProgress::Complete(ScanQrResult::new_qr(data)); } // All frames have been processed, UR decoder is not complete or empty, @@ -162,11 +157,18 @@ fn app_main(cx: AppContext, ui: AppWindow) { let haptics = Rc::new(HapticsApi::default()); let state = { - let mut scanner = Quirc::new(); - scanner.resize(CAMERA_WIDTH, CAMERA_HEIGHT); - let frame_luma8 = [0u8; CAMERA_WIDTH * CAMERA_HEIGHT]; + let hints = DecodeHints { + PossibleFormats: Some(HashSet::from([BarcodeFormat::QR_CODE])), + TryHarder: Some(true), + AlsoInverted: Some(true), + ..Default::default() + }; + let mut scanner = MultiFormatReader::default(); + scanner.set_hints(&hints); + let luma_source = + Luma8LuminanceSource::with_empty_image(CAMERA_WIDTH as usize, CAMERA_HEIGHT as usize); let state = - AppState { status: ScanStatus::Idle, scanner, frame_luma8, ur_decoder: UrDecoder::default() }; + AppState { status: ScanStatus::Idle, scanner, luma_source, ur_decoder: UrDecoder::default() }; StoredValue::new(state) }; @@ -233,7 +235,7 @@ fn app_main(cx: AppContext, ui: AppWindow) { debug!("Fetching frame for QR detection"); { - let luma8 = &mut state.frame_luma8; + let luma8 = &mut state.luma_source.get_matrix_mut(); #[cfg(keyos)] { let Ok(frame) = camera_api.get_frame_mirror() else {