From ce9c8b1b6e23de653ee6bf857566fd95f897a184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com> Date: Sat, 27 Jun 2026 14:53:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=A7=A3=E7=A0=81=E5=99=A83=E4=B8=AAbug?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20=E2=80=94=20=E7=BC=96=E7=A0=81=E2=86=92?= =?UTF-8?q?=E8=A7=A3=E7=A0=81=E5=BE=80=E8=BF=94=E6=81=A2=E5=A4=8D=20+=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BE=80=E8=BF=94=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. detect.rs: remove()索引bug — 元素前移后索引未更新,改用每次remove(0) 2. detect.rs: 版本估算公式修正 — (dist-14)/4 → (dist-10)/4,符合ZXing公式 3. extract.rs: 移除显式col 6跳过 — read_module已自动跳过保留区,显式skip导致列配对错位/行序反转 新增 test_roundtrip_png: 矩阵往返 + PNG往返双验证 --- core/src/decoder/detect.rs | 11 +++++++---- core/src/decoder/extract.rs | 7 ++----- core/src/qr.rs | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/core/src/decoder/detect.rs b/core/src/decoder/detect.rs index 0fc1c62..7cec3f8 100644 --- a/core/src/decoder/detect.rs +++ b/core/src/decoder/detect.rs @@ -145,7 +145,7 @@ pub(crate) fn estimate_version_from_tl_tr(tl: (f64, f64), tr: (f64, f64), module let dy = tr.1 - tl.1; let dist_px = (dx * dx + dy * dy).sqrt() as f32; let dist_modules = dist_px / module_size as f32; - let ver = ((dist_modules as i32 - 14) / 4) as u8; + let ver = ((dist_modules as i32 - 10) / 4) as u8; ver.clamp(1, 40) } @@ -226,9 +226,10 @@ fn find_finders(gray: &[Vec]) -> Option<[FinderMatch; 3]> { finders.swap(1, 2); } + // remove 会使后续元素前移,必须每次 remove(0) let f0 = finders.remove(0); - let f1 = finders.remove(1); - let f2 = finders.remove(2); + let f1 = finders.remove(0); + let f2 = finders.remove(0); // ZXing 几何验证:module_size 一致性 + 勾股定理 if !validate_finder_geometry(&[&f0, &f1, &f2]) { @@ -303,7 +304,9 @@ pub(crate) fn detect_and_extract( let dy = tr.cy as f64 - tl.cy as f64; let dist_px = (dx * dx + dy * dy).sqrt() as f32; let dist_modules = dist_px / module_size as f32; - let ver = ((dist_modules as i32 - 14) / 4) as u8; + // ZXing 公式: totalModules = dist/moduleSize + 7, version = (totalModules - 17) / 4 + // 简化为: version = (dist_modules + 7 - 17) / 4 = (dist_modules - 10) / 4 + let ver = ((dist_modules as i32 - 10) / 4) as u8; let version = ver.clamp(1, 40); let size = 17 + version as usize * 4; diff --git a/core/src/decoder/extract.rs b/core/src/decoder/extract.rs index 2aa0481..33904d4 100644 --- a/core/src/decoder/extract.rs +++ b/core/src/decoder/extract.rs @@ -32,11 +32,8 @@ fn extract_bits(matrix: &Matrix, total_codewords: usize) -> Vec { } col -= 2; going_up = !going_up; - - // 跳过垂直时序图案列(col 6) - if col == 6 { - col -= 1; - } + // 垂直时序图案列(col 6)由 read_module 自动跳过保留区, + // 无需显式 skip,否则会导致列配对错位、行序反转 } bits.truncate(target_bits); diff --git a/core/src/qr.rs b/core/src/qr.rs index 6d64d11..e0717b8 100644 --- a/core/src/qr.rs +++ b/core/src/qr.rs @@ -419,4 +419,26 @@ mod tests { assert!(svg.contains("#FF0000")); assert!(svg.contains("#0000FF")); } + + #[test] + fn test_roundtrip_png() { + let config = QrConfig { + level: EcLevel::M, + version: VersionMode::Auto, + margin: 4, + }; + let qr = QrCode::encode("Hello World", config).unwrap(); + + // 直接从矩阵解码 + let matrix: Vec> = qr.modules().to_vec(); + let result = crate::decoder::decode_matrix(&matrix) + .expect("矩阵解码往返失败"); + assert_eq!(result.text, "Hello World"); + + // PNG 往返 + let png_bytes = qr.to_png_bytes(8, None).unwrap(); + let result = crate::decoder::decode_image(&png_bytes) + .expect("PNG 解码往返失败"); + assert_eq!(result.text, "Hello World"); + } }