use std::{collections::HashMap, rc::Rc};
use once_cell::sync::Lazy;
use crate::{
common::{
reedsolomon::get_predefined_genericgf, reedsolomon::PredefinedGenericGF,
reedsolomon::ReedSolomonDecoder, BitMatrix, DecoderRXingResult, Result,
},
DecodingHintDictionary, Exceptions,
};
use super::{decoded_bit_stream_parser, BitMatrixParser, DataBlock, QRCodeDecoderMetaData};
static RS_DECODER: Lazy<ReedSolomonDecoder> = Lazy::new(|| {
ReedSolomonDecoder::new(get_predefined_genericgf(
PredefinedGenericGF::QrCodeField256,
))
});
pub fn decode_bool_array(image: &Vec<Vec<bool>>) -> Result<DecoderRXingResult> {
decode_bool_array_with_hints(image, &HashMap::new())
}
pub fn decode_bool_array_with_hints(
image: &Vec<Vec<bool>>,
hints: &DecodingHintDictionary,
) -> Result<DecoderRXingResult> {
decode_bitmatrix_with_hints(&BitMatrix::parse_bools(image), hints)
}
pub fn decode_bitmatrix(bits: &BitMatrix) -> Result<DecoderRXingResult> {
decode_bitmatrix_with_hints(bits, &HashMap::new())
}
pub fn decode_bitmatrix_with_hints(
bits: &BitMatrix,
hints: &DecodingHintDictionary,
) -> Result<DecoderRXingResult> {
let mut parser = BitMatrixParser::new(bits.clone())?;
let mut fe = None;
let mut ce = None;
match decode_bitmatrix_parser_with_hints(&mut parser, hints) {
Ok(ok) => return Ok(ok),
Err(er) => match er {
Exceptions::FormatException(_) => fe = Some(er),
Exceptions::ChecksumException(_) => ce = Some(er),
_ => return Err(er),
},
}
let mut trying = || -> Result<DecoderRXingResult> {
parser.remask()?;
parser.setMirror(true);
parser.readVersion()?;
parser.readFormatInformation()?;
parser.mirror();
let mut result = decode_bitmatrix_parser_with_hints(&mut parser, hints)?;
result.setOther(Some(Rc::new(QRCodeDecoderMetaData::new(true))));
Ok(result)
};
match trying() {
Ok(res) => Ok(res),
Err(er) => match er {
Exceptions::FormatException(_) | Exceptions::ChecksumException(_) => {
if let Some(fe) = fe {
Err(fe)
} else {
Err(ce.unwrap_or(Exceptions::CHECKSUM))
}
}
_ => Err(er),
},
}
}
fn decode_bitmatrix_parser_with_hints(
parser: &mut BitMatrixParser,
hints: &DecodingHintDictionary,
) -> Result<DecoderRXingResult> {
let version = parser.readVersion()?;
let ecLevel = parser.readFormatInformation()?.getErrorCorrectionLevel();
let codewords = parser.readCodewords()?;
let dataBlocks = DataBlock::getDataBlocks(&codewords, version, ecLevel)?;
let totalBytes = dataBlocks.iter().fold(0, |acc, dataBlock| {
acc + dataBlock.getNumDataCodewords() as usize
});
let mut resultBytes = vec![0u8; totalBytes];
let mut resultOffset = 0;
for dataBlock in &dataBlocks {
let mut codewordBytes = dataBlock.getCodewords().to_vec();
let numDataCodewords = dataBlock.getNumDataCodewords() as usize;
correctErrors(&mut codewordBytes, numDataCodewords)?;
for codeword_byte in codewordBytes.iter().take(numDataCodewords) {
resultBytes[resultOffset] = *codeword_byte;
resultOffset += 1;
}
}
decoded_bit_stream_parser::decode(&resultBytes, version, ecLevel, hints)
}
fn correctErrors(codewordBytes: &mut [u8], numDataCodewords: usize) -> Result<()> {
let numCodewords = codewordBytes.len();
let mut codewordsInts = vec![0u8; numCodewords];
codewordsInts[..numCodewords].copy_from_slice(&codewordBytes[..numCodewords]);
let mut sending_code_words: Vec<i32> = codewordsInts.iter().map(|x| *x as i32).collect();
if let Err(Exceptions::ReedSolomonException(error_str)) = RS_DECODER.decode(
&mut sending_code_words,
(codewordBytes.len() - numDataCodewords) as i32,
) {
return Err(Exceptions::ChecksumException(error_str));
}
for (code_word, sent_code_word) in codewordBytes
.iter_mut()
.zip(sending_code_words.iter())
.take(numDataCodewords)
{
*code_word = *sent_code_word as u8;
}
Ok(())
}