// go-qrcode
// Copyright 2014 Tom Harwood
package qrcode
import (
bitset "github.com/skip2/go-qrcode/bitset"
)
type regularSymbol struct {
version qrCodeVersion
mask int
data *bitset.Bitset
symbol *symbol
size int
}
// Abbreviated true/false.
const (
b0 = false
b1 = true
)
var (
alignmentPatternCenter = [][]int{
{}, // Version 0 doesn't exist.
{}, // Version 1 doesn't use alignment patterns.
{6, 18},
{6, 22},
{6, 26},
{6, 30},
{6, 34},
{6, 22, 38},
{6, 24, 42},
{6, 26, 46},
{6, 28, 50},
{6, 30, 54},
{6, 32, 58},
{6, 34, 62},
{6, 26, 46, 66},
{6, 26, 48, 70},
{6, 26, 50, 74},
{6, 30, 54, 78},
{6, 30, 56, 82},
{6, 30, 58, 86},
{6, 34, 62, 90},
{6, 28, 50, 72, 94},
{6, 26, 50, 74, 98},
{6, 30, 54, 78, 102},
{6, 28, 54, 80, 106},
{6, 32, 58, 84, 110},
{6, 30, 58, 86, 114},
{6, 34, 62, 90, 118},
{6, 26, 50, 74, 98, 122},
{6, 30, 54, 78, 102, 126},
{6, 26, 52, 78, 104, 130},
{6, 30, 56, 82, 108, 134},
{6, 34, 60, 86, 112, 138},
{6, 30, 58, 86, 114, 142},
{6, 34, 62, 90, 118, 146},
{6, 30, 54, 78, 102, 126, 150},
{6, 24, 50, 76, 102, 128, 154},
{6, 28, 54, 80, 106, 132, 158},
{6, 32, 58, 84, 110, 136, 162},
{6, 26, 54, 82, 110, 138, 166},
{6, 30, 58, 86, 114, 142, 170},
}
finderPattern = [][]bool{
{b1, b1, b1, b1, b1, b1, b1},
{b1, b0, b0, b0, b0, b0, b1},
{b1, b0, b1, b1, b1, b0, b1},
{b1, b0, b1, b1, b1, b0, b1},
{b1, b0, b1, b1, b1, b0, b1},
{b1, b0, b0, b0, b0, b0, b1},
{b1, b1, b1, b1, b1, b1, b1},
}
finderPatternSize = 7
finderPatternHorizontalBorder = [][]bool{
{b0, b0, b0, b0, b0, b0, b0, b0},
}
finderPatternVerticalBorder = [][]bool{
{b0},
{b0},
{b0},
{b0},
{b0},
{b0},
{b0},
{b0},
}
alignmentPattern = [][]bool{
{b1, b1, b1, b1, b1},
{b1, b0, b0, b0, b1},
{b1, b0, b1, b0, b1},
{b1, b0, b0, b0, b1},
{b1, b1, b1, b1, b1},
}
)
func buildRegularSymbol(version qrCodeVersion, mask int,
data *bitset.Bitset) (*symbol, error) {
m := ®ularSymbol{
version: version,
mask: mask,
data: data,
symbol: newSymbol(version.symbolSize(), version.quietZoneSize()),
size: version.symbolSize(),
}
m.addFinderPatterns()
m.addAlignmentPatterns()
m.addTimingPatterns()
m.addFormatInfo()
m.addVersionInfo()
ok, err := m.addData()
if !ok {
return nil, err
}
return m.symbol, nil
}
func (m *regularSymbol) addFinderPatterns() {
fpSize := finderPatternSize
fp := finderPattern
fpHBorder := finderPatternHorizontalBorder
fpVBorder := finderPatternVerticalBorder
// Top left Finder Pattern.
m.symbol.set2dPattern(0, 0, fp)
m.symbol.set2dPattern(0, fpSize, fpHBorder)
m.symbol.set2dPattern(fpSize, 0, fpVBorder)
// Top right Finder Pattern.
m.symbol.set2dPattern(m.size-fpSize, 0, fp)
m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder)
m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder)
// Bottom left Finder Pattern.
m.symbol.set2dPattern(0, m.size-fpSize, fp)
m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder)
m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder)
}
func (m *regularSymbol) addAlignmentPatterns() {
for _, x := range alignmentPatternCenter[m.version.version] {
for _, y := range alignmentPatternCenter[m.version.version] {
if !m.symbol.empty(x, y) {
continue
}
m.symbol.set2dPattern(x-2, y-2, alignmentPattern)
}
}
}
func (m *regularSymbol) addTimingPatterns() {
value := true
for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ {
m.symbol.set(i, finderPatternSize-1, value)
m.symbol.set(finderPatternSize-1, i, value)
value = !value
}
}
func (m *regularSymbol) addFormatInfo() {
fpSize := finderPatternSize
l := formatInfoLengthBits - 1
f := m.version.formatInfo(m.mask)
// Bits 0-7, under the top right finder pattern.
for i := 0; i <= 7; i++ {
m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i))
}
// Bits 0-5, right of the top left finder pattern.
for i := 0; i <= 5; i++ {
m.symbol.set(fpSize+1, i, f.At(l-i))
}
// Bits 6-8 on the corner of the top left finder pattern.
m.symbol.set(fpSize+1, fpSize, f.At(l-6))
m.symbol.set(fpSize+1, fpSize+1, f.At(l-7))
m.symbol.set(fpSize, fpSize+1, f.At(l-8))
// Bits 9-14 on the underside of the top left finder pattern.
for i := 9; i <= 14; i++ {
m.symbol.set(14-i, fpSize+1, f.At(l-i))
}
// Bits 8-14 on the right side of the bottom left finder pattern.
for i := 8; i <= 14; i++ {
m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i))
}
// Always dark symbol.
m.symbol.set(fpSize+1, m.size-fpSize-1, true)
}
func (m *regularSymbol) addVersionInfo() {
fpSize := finderPatternSize
v := m.version.versionInfo()
l := versionInfoLengthBits - 1
if v == nil {
return
}
for i := 0; i < v.Len(); i++ {
// Above the bottom left finder pattern.
m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i))
// Left of the top right finder pattern.
m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i))
}
}
type direction uint8
const (
up direction = iota
down
)
func (m *regularSymbol) addData() (bool, error) {
xOffset := 1
dir := up
x := m.size - 2
y := m.size - 1
for i := 0; i < m.data.Len(); i++ {
var mask bool
switch m.mask {
case 0:
mask = (y+x+xOffset)%2 == 0
case 1:
mask = y%2 == 0
case 2:
mask = (x+xOffset)%3 == 0
case 3:
mask = (y+x+xOffset)%3 == 0
case 4:
mask = (y/2+(x+xOffset)/3)%2 == 0
case 5:
mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0
case 6:
mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0
case 7:
mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0
}
// != is equivalent to XOR.
m.symbol.set(x+xOffset, y, mask != m.data.At(i))
if i == m.data.Len()-1 {
break
}
// Find next free bit in the symbol.
for {
if xOffset == 1 {
xOffset = 0
} else {
xOffset = 1
if dir == up {
if y > 0 {
y--
} else {
dir = down
x -= 2
}
} else {
if y < m.size-1 {
y++
} else {
dir = up
x -= 2
}
}
}
// Skip over the vertical timing pattern entirely.
if x == 5 {
x--
}
if m.symbol.empty(x+xOffset, y) {
break
}
}
}
return true, nil
}