//! 从 QR 矩阵数据区域蛇形读取码字比特 //! //! 读取顺序严格对应 `placement.rs::place_data` 的写入顺序: //! 从右下角开始,两列一组上下交替扫描,跳过保留模块。 use crate::matrix::grid::Matrix; /// 从矩阵非保留区域蛇形读取码字比特 fn extract_bits(matrix: &Matrix, total_codewords: usize) -> Vec { let size = matrix.size as usize; let target_bits = total_codewords * 8; let mut bits: Vec = 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, 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 { 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 { 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); // 至少有数据 } }