169 lines
4.5 KiB
Go
169 lines
4.5 KiB
Go
package decoder
|
|
|
|
import (
|
|
"github.com/makiuchi-d/gozxing"
|
|
"github.com/makiuchi-d/gozxing/common"
|
|
"github.com/makiuchi-d/gozxing/common/reedsolomon"
|
|
)
|
|
|
|
type Decoder struct {
|
|
rsDecoder *reedsolomon.ReedSolomonDecoder
|
|
}
|
|
|
|
func NewDecoder() *Decoder {
|
|
return &Decoder{
|
|
rsDecoder: reedsolomon.NewReedSolomonDecoder(reedsolomon.GenericGF_QR_CODE_FIELD_256),
|
|
}
|
|
}
|
|
|
|
func (this *Decoder) DecodeBoolMapWithoutHint(image [][]bool) (*common.DecoderResult, error) {
|
|
return this.DecodeBoolMap(image, nil)
|
|
}
|
|
|
|
func (this *Decoder) DecodeBoolMap(image [][]bool, hints map[gozxing.DecodeHintType]interface{}) (*common.DecoderResult, error) {
|
|
bits, e := gozxing.ParseBoolMapToBitMatrix(image)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
return this.Decode(bits, hints)
|
|
}
|
|
|
|
func (this *Decoder) DecodeWithoutHint(bits *gozxing.BitMatrix) (*common.DecoderResult, error) {
|
|
return this.Decode(bits, nil)
|
|
}
|
|
|
|
func (this *Decoder) Decode(bits *gozxing.BitMatrix, hints map[gozxing.DecodeHintType]interface{}) (*common.DecoderResult, error) {
|
|
|
|
// Construct a parser and read version, error-correction level
|
|
parser, e := NewBitMatrixParser(bits)
|
|
if e != nil {
|
|
return nil, gozxing.WrapFormatException(e)
|
|
}
|
|
var fece gozxing.ReaderException
|
|
|
|
result, e := this.decode(parser, hints)
|
|
if e == nil {
|
|
return result, nil
|
|
}
|
|
|
|
switch e.(type) {
|
|
case gozxing.FormatException, gozxing.ChecksumException:
|
|
fece = e.(gozxing.ReaderException)
|
|
}
|
|
e = nil
|
|
|
|
// Revert the bit matrix
|
|
parser.Remask()
|
|
|
|
// Will be attempting a mirrored reading of the version and format info.
|
|
parser.SetMirror(true)
|
|
|
|
if e == nil {
|
|
// Preemptively read the version.
|
|
_, e = parser.ReadVersion()
|
|
}
|
|
|
|
if e == nil {
|
|
// Preemptively read the format information.
|
|
_, e = parser.ReadFormatInformation()
|
|
}
|
|
|
|
if e == nil {
|
|
/*
|
|
* Since we're here, this means we have successfully detected some kind
|
|
* of version and format information when mirrored. This is a good sign,
|
|
* that the QR code may be mirrored, and we should try once more with a
|
|
* mirrored content.
|
|
*/
|
|
// Prepare for a mirrored reading.
|
|
parser.Mirror()
|
|
}
|
|
|
|
if e == nil {
|
|
result, e = this.decode(parser, hints)
|
|
}
|
|
|
|
if e == nil {
|
|
// Success! Notify the caller that the code was mirrored.
|
|
result.SetOther(NewQRCodeDecoderMetaData(true))
|
|
return result, nil
|
|
}
|
|
|
|
// `e` is not nil
|
|
switch e.(type) {
|
|
case gozxing.FormatException, gozxing.ChecksumException:
|
|
// Throw the exception from the original reading
|
|
return nil, fece
|
|
default:
|
|
return nil, e
|
|
}
|
|
}
|
|
|
|
func (this *Decoder) decode(parser *BitMatrixParser, hints map[gozxing.DecodeHintType]interface{}) (*common.DecoderResult, error) {
|
|
version, e := parser.ReadVersion()
|
|
if e != nil {
|
|
return nil, gozxing.WrapFormatException(e)
|
|
}
|
|
formatinfo, e := parser.ReadFormatInformation()
|
|
if e != nil {
|
|
return nil, gozxing.WrapFormatException(e)
|
|
}
|
|
ecLevel := formatinfo.GetErrorCorrectionLevel()
|
|
|
|
// Read codewords
|
|
codewords, e := parser.ReadCodewords()
|
|
if e != nil {
|
|
return nil, gozxing.WrapFormatException(e)
|
|
}
|
|
// Separate into data blocks
|
|
dataBlocks, e := DataBlock_GetDataBlocks(codewords, version, ecLevel)
|
|
if e != nil {
|
|
return nil, gozxing.WrapFormatException(e)
|
|
}
|
|
|
|
// Count total number of data bytes
|
|
totalBytes := 0
|
|
for _, dataBlock := range dataBlocks {
|
|
totalBytes += dataBlock.GetNumDataCodewords()
|
|
}
|
|
resultBytes := make([]byte, totalBytes)
|
|
resultOffset := 0
|
|
|
|
// Error-correct and copy data blocks together into a stream of bytes
|
|
for _, dataBlock := range dataBlocks {
|
|
codewordBytes := dataBlock.GetCodewords()
|
|
numDataCodewords := dataBlock.GetNumDataCodewords()
|
|
e := this.correctErrors(codewordBytes, numDataCodewords)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
for i := 0; i < numDataCodewords; i++ {
|
|
resultBytes[resultOffset] = codewordBytes[i]
|
|
resultOffset++
|
|
}
|
|
}
|
|
|
|
// Decode the contents of that stream of bytes
|
|
return DecodedBitStreamParser_Decode(resultBytes, version, ecLevel, hints)
|
|
}
|
|
|
|
func (this *Decoder) correctErrors(codewordBytes []byte, numDataCodewords int) error {
|
|
numCodewords := len(codewordBytes)
|
|
// First read into an array of ints
|
|
codewordsInts := make([]int, numCodewords)
|
|
for i := 0; i < numCodewords; i++ {
|
|
codewordsInts[i] = int(codewordBytes[i] & 0xFF)
|
|
}
|
|
|
|
e := this.rsDecoder.Decode(codewordsInts, numCodewords-numDataCodewords)
|
|
if e != nil {
|
|
return gozxing.WrapChecksumException(e)
|
|
}
|
|
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
|
// We don't care about errors in the error-correction codewords
|
|
for i := 0; i < numDataCodewords; i++ {
|
|
codewordBytes[i] = byte(codewordsInts[i])
|
|
}
|
|
return nil
|
|
}
|