147 lines
3.0 KiB
Go
147 lines
3.0 KiB
Go
package goqr
|
|
|
|
// qrCode is used to return information about detected QR codes
|
|
// in the input image.
|
|
type qrCode struct {
|
|
// The four corners of the QR-code, from top left, clockwise
|
|
corners [4]point
|
|
|
|
// The number of cells across in the QR-code. The cell bitmap
|
|
// is a bitmask giving the actual values of cells. If the cell
|
|
// at (x, y) is black, then the following bit is set:
|
|
//
|
|
// cell_bitmap[i >> 3] & (1 << (i & 7))
|
|
//
|
|
// where i = (y * size) + x.
|
|
//
|
|
|
|
size int
|
|
cellBitmap [qrMaxBimap]uint8
|
|
}
|
|
|
|
// QRData holds the decoded QR-code data
|
|
type QRData struct {
|
|
// Various parameters of the QR-code. These can mostly be
|
|
// ignored if you only care about the data.
|
|
Version int
|
|
EccLevel int
|
|
Mask int
|
|
|
|
// This field is the highest-valued data type found in the QR code.
|
|
DataType int
|
|
|
|
// Data Payload. For the Kanji datatype, Payload is encoded as
|
|
// Shift-JIS. For all other datatypes, Payload is ASCII text.
|
|
Payload []uint8
|
|
|
|
// ECI assignment number
|
|
Eci uint32
|
|
}
|
|
|
|
func gridBit(code *qrCode, x, y int) int {
|
|
p := uint(y*code.size + x)
|
|
return int((code.cellBitmap[p>>3])>>(p&7)) & 1
|
|
}
|
|
|
|
func readFormat(code *qrCode, data *QRData, which int) error {
|
|
format := uint16(0)
|
|
if which != 0 {
|
|
for i := 0; i < 7; i++ {
|
|
format = (format << 1) | uint16(gridBit(code, 8, code.size-1-i))
|
|
}
|
|
for i := 0; i < 8; i++ {
|
|
format = (format << 1) | uint16(gridBit(code, code.size-8+i, 8))
|
|
}
|
|
} else {
|
|
xs := [15]int{8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0}
|
|
ys := [15]int{0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8}
|
|
for i := 14; i >= 0; i-- {
|
|
format = (format << 1) | uint16(gridBit(code, xs[i], ys[i]))
|
|
}
|
|
}
|
|
|
|
format ^= 0x5412
|
|
err := correctFormat(&format)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fdata := format >> 10
|
|
data.EccLevel = int(fdata) >> 3
|
|
data.Mask = int(fdata) & 7
|
|
return nil
|
|
}
|
|
|
|
func readBit(code *qrCode, data *QRData, ds *datastream, i, j int) {
|
|
bitpos := ds.dataBits & 7
|
|
bytepos := ds.dataBits >> 3
|
|
v := gridBit(code, j, i)
|
|
|
|
if maskBit(data.Mask, i, j) != 0 {
|
|
v ^= 1
|
|
}
|
|
|
|
if v != 0 {
|
|
ds.raw[bytepos] |= 0x80 >> uint32(bitpos)
|
|
}
|
|
ds.dataBits++
|
|
}
|
|
|
|
func readData(code *qrCode, data *QRData, ds *datastream) {
|
|
y := code.size - 1
|
|
x := code.size - 1
|
|
dir := -1
|
|
for x > 0 {
|
|
if x == 6 {
|
|
x--
|
|
}
|
|
|
|
if 0 == reservedCell(data.Version, y, x) {
|
|
readBit(code, data, ds, y, x)
|
|
}
|
|
|
|
if 0 == reservedCell(data.Version, y, x-1) {
|
|
readBit(code, data, ds, y, x-1)
|
|
}
|
|
y += dir
|
|
if y < 0 || y >= code.size {
|
|
dir = -dir
|
|
x -= 2
|
|
y += dir
|
|
}
|
|
}
|
|
}
|
|
|
|
func decode(code *qrCode, data *QRData) error {
|
|
var ds datastream
|
|
if (code.size-17)%4 != 0 {
|
|
return ErrInvalidGridSize
|
|
}
|
|
data.Version = (code.size - 17) / 4
|
|
|
|
if data.Version < 1 || data.Version > qrMaxVersion {
|
|
return ErrInvalidVersion
|
|
}
|
|
|
|
// Read format information -- try both locations
|
|
err := readFormat(code, data, 0)
|
|
if err != nil {
|
|
err = readFormat(code, data, 1)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
readData(code, data, &ds)
|
|
|
|
err = codestreamEcc(data, &ds)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = decodePayload(data, &ds)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|