ce9c8b1b6e
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往返双验证
91 lines
3.0 KiB
Rust
91 lines
3.0 KiB
Rust
//! 从 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); // 至少有数据
|
||
}
|
||
}
|