fix: 解码器3个bug修复 — 编码→解码往返恢复 + 新增往返测试
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往返双验证
This commit is contained in:
@@ -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<bool>]) -> 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;
|
||||
|
||||
@@ -32,11 +32,8 @@ fn extract_bits(matrix: &Matrix, total_codewords: usize) -> Vec<bool> {
|
||||
}
|
||||
col -= 2;
|
||||
going_up = !going_up;
|
||||
|
||||
// 跳过垂直时序图案列(col 6)
|
||||
if col == 6 {
|
||||
col -= 1;
|
||||
}
|
||||
// 垂直时序图案列(col 6)由 read_module 自动跳过保留区,
|
||||
// 无需显式 skip,否则会导致列配对错位、行序反转
|
||||
}
|
||||
|
||||
bits.truncate(target_bits);
|
||||
|
||||
@@ -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<Vec<bool>> = 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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user