Files
QRGen/core/src/decoder/extract.rs
T
Serendipity ce9c8b1b6e 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往返双验证
2026-06-27 14:53:48 +08:00

91 lines
3.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 从 QR 矩阵数据区域蛇形读取码字比特
//!
//! 读取顺序严格对应 `placement.rs::place_data` 的写入顺序:
//! 从右下角开始,两列一组上下交替扫描,跳过保留模块。
use crate::matrix::grid::Matrix;
/// 从矩阵非保留区域蛇形读取码字比特
fn extract_bits(matrix: &Matrix, total_codewords: usize) -> Vec<bool> {
let size = matrix.size as usize;
let target_bits = total_codewords * 8;
let mut bits: Vec<bool> = Vec::with_capacity(target_bits);
let mut col = (size - 1) as i16;
let mut going_up = true;
while col >= 0 && bits.len() < target_bits {
let actual_col = col as usize;
if going_up {
for row in (0..size).rev() {
read_module(matrix, &mut bits, actual_col, row);
if actual_col > 0 {
read_module(matrix, &mut bits, actual_col - 1, row);
}
}
} else {
for row in 0..size {
read_module(matrix, &mut bits, actual_col, row);
if actual_col > 0 {
read_module(matrix, &mut bits, actual_col - 1, row);
}
}
}
col -= 2;
going_up = !going_up;
// 垂直时序图案列(col 6)由 read_module 自动跳过保留区,
// 无需显式 skip,否则会导致列配对错位、行序反转
}
bits.truncate(target_bits);
bits
}
/// 读取单个非保留模块,追加 bool 到 bits
fn read_module(matrix: &Matrix, bits: &mut Vec<bool>, x: usize, y: usize) {
let xu = x as u8;
let yu = y as u8;
if xu < matrix.size && yu < matrix.size && !matrix.is_reserved(xu, yu) {
bits.push(matrix.get(xu, yu));
}
}
/// 将布尔比特打包为 u8 码字(MSB 优先)
fn bits_to_bytes(bits: &[bool]) -> Vec<u8> {
bits.chunks(8)
.map(|chunk| {
let mut byte = 0u8;
for &b in chunk {
byte = (byte << 1) | (b as u8);
}
// 不足 8 位的 chunk 左对齐
byte <<= 8 - chunk.len();
byte
})
.collect()
}
/// 从矩阵提取数据码字
///
/// *`total_codewords` 为数据码字总数(来自 EcInfo.total_codewords*
pub(crate) fn extract_codewords(matrix: &Matrix, total_codewords: usize) -> Vec<u8> {
let bits = extract_bits(matrix, total_codewords);
bits_to_bytes(&bits)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::qr::{QrCode, QrConfig};
use crate::version::get_data_capacity;
#[test]
fn test_extract_roundtrip_simple() {
let qr = QrCode::encode("HELLO WORLD", QrConfig::default()).unwrap();
let cap = get_data_capacity(qr.version, qr.level) as usize;
let codewords = extract_codewords(qr.matrix(), cap);
assert!(!codewords.is_empty());
// 前 11 字节编码 "HELLO WORLD"byte 模式 4b+8b+88b=100bits≈13字节)
assert!(codewords.len() >= 5); // 至少有数据
}
}