Files

243 lines
5.1 KiB
Go

package input
// qrCodeGrid is a symbol under construction; fixed marks function modules.
type qrCodeGrid struct {
v int
dark, fixed [][]bool
}
func newQRCodeGrid(v int) *qrCodeGrid {
n := 4*v + 17
g := &qrCodeGrid{v: v, dark: make([][]bool, n), fixed: make([][]bool, n)}
for i := range n {
g.dark[i], g.fixed[i] = make([]bool, n), make([]bool, n)
}
for i := range n {
g.set(6, i, i%2 == 0)
g.set(i, 6, i%2 == 0)
}
g.finder(3, 3)
g.finder(n-4, 3)
g.finder(3, n-4)
al := qrCodeAlignment(v)
for i, x := range al {
for j, y := range al {
if i == 0 && j == 0 || i == 0 && j == len(al)-1 || i == len(al)-1 && j == 0 {
continue
}
for dy := -2; dy <= 2; dy++ {
for dx := -2; dx <= 2; dx++ {
g.set(x+dx, y+dy, max(qrCodeAbs(dx), qrCodeAbs(dy)) != 1)
}
}
}
}
// Reserve format and version areas; apply fills them.
g.format(-1, 0)
g.version(false)
return g
}
func qrCodeAbs(v int) int { return max(v, -v) }
func (g *qrCodeGrid) set(x, y int, dark bool) {
g.dark[y][x], g.fixed[y][x] = dark, true
}
// finder draws a finder pattern and its separator centered at (cx, cy).
func (g *qrCodeGrid) finder(cx, cy int) {
n := len(g.dark)
for dy := -4; dy <= 4; dy++ {
for dx := -4; dx <= 4; dx++ {
x, y := cx+dx, cy+dy
if x < 0 || y < 0 || x >= n || y >= n {
continue
}
d := max(qrCodeAbs(dx), qrCodeAbs(dy))
g.set(x, y, d != 2 && d != 4)
}
}
}
// format writes the 15 format bits and the dark module; levelBits -1 leaves them light.
func (g *qrCodeGrid) format(levelBits, mask int) {
data := levelBits<<3 | mask
rem := data
for range 10 {
rem = rem<<1 ^ (rem>>9)*0x537
}
b := (data<<10 | rem) ^ 0x5412
bit := func(i int) bool { return levelBits >= 0 && b>>i&1 == 1 }
n := len(g.dark)
for i := 0; i <= 5; i++ {
g.set(8, i, bit(i))
}
g.set(8, 7, bit(6))
g.set(8, 8, bit(7))
g.set(7, 8, bit(8))
for i := 9; i < 15; i++ {
g.set(14-i, 8, bit(i))
}
for i := range 8 {
g.set(n-1-i, 8, bit(i))
}
for i := 8; i < 15; i++ {
g.set(8, n-15+i, bit(i))
}
g.set(8, n-8, levelBits >= 0)
}
// version writes the 18 version bits from version 7 on, or light ones.
func (g *qrCodeGrid) version(on bool) {
if g.v < 7 {
return
}
rem := g.v
for range 12 {
rem = rem<<1 ^ (rem>>11)*0x1F25
}
b := g.v<<12 | rem
n := len(g.dark)
for i := range 18 {
dark := on && b>>i&1 == 1
a, c := n-11+i%3, i/3
g.set(a, c, dark)
g.set(c, a, dark)
}
}
// place lays the codewords in the zigzag from the bottom right; remainder bits stay light.
func (g *qrCodeGrid) place(words []byte) {
n := len(g.dark)
i := 0
for right := n - 1; right >= 1; right -= 2 {
if right == 6 {
right = 5
}
for vert := range n {
for j := range 2 {
x := right - j
up := (right+1)&2 == 0
y := vert
if up {
y = n - 1 - vert
}
if g.fixed[y][x] || i >= len(words)*8 {
continue
}
g.dark[y][x] = words[i/8]>>(7-i%8)&1 == 1
i++
}
}
}
}
func (g *qrCodeGrid) clone() *qrCodeGrid {
t := &qrCodeGrid{v: g.v, dark: make([][]bool, len(g.dark)), fixed: g.fixed}
for i, row := range g.dark {
t.dark[i] = append([]bool(nil), row...)
}
return t
}
// apply XORs mask over the data modules; the information areas stay light, as 7.8.3 evaluates them.
func (g *qrCodeGrid) apply(mask int) {
for y, row := range g.dark {
for x := range row {
if g.fixed[y][x] {
continue
}
var flip bool
switch mask {
case 0:
flip = (x+y)%2 == 0
case 1:
flip = y%2 == 0
case 2:
flip = x%3 == 0
case 3:
flip = (x+y)%3 == 0
case 4:
flip = (x/3+y/2)%2 == 0
case 5:
flip = x*y%2+x*y%3 == 0
case 6:
flip = (x*y%2+x*y%3)%2 == 0
case 7:
flip = ((x+y)%2+x*y%3)%2 == 0
}
row[x] = row[x] != flip
}
}
}
// penalty scores the symbol by the four rules of ISO/IEC 18004 7.8.3.
func (g *qrCodeGrid) penalty() int {
n := len(g.dark)
p, dark := 0, 0
row, col := make([]bool, n), make([]bool, n)
for i := range n {
for j := range n {
row[j], col[j] = g.dark[i][j], g.dark[j][i]
if row[j] {
dark++
}
if i > 0 && j > 0 && row[j] == row[j-1] && row[j] == g.dark[i-1][j] && row[j] == g.dark[i-1][j-1] {
p += 3
}
}
p += qrCodeRuns(row) + qrCodeFinders(row) + qrCodeRuns(col) + qrCodeFinders(col)
}
return p + 10*(qrCodeAbs(dark*100-50*n*n)/(5*n*n))
}
// qrCodeRuns scores runs of five or more equal modules.
func qrCodeRuns(seq []bool) int {
p, run := 0, 1
for j := 1; j <= len(seq); j++ {
if j < len(seq) && seq[j] == seq[j-1] {
run++
continue
}
if run >= 5 {
p += run - 2
}
run = 1
}
return p
}
// qrCodeFinders scores 1:1:3:1:1 patterns with four light modules on a side.
func qrCodeFinders(seq []bool) int {
core := [7]bool{true, false, true, true, true, false, true}
n, p := len(seq), 0
light := func(a, b int) bool {
for i := max(a, 0); i < min(b, n); i++ {
if seq[i] {
return false
}
}
return true
}
for idx := 0; idx+7 <= n; {
match := true
for i, d := range core {
if seq[idx+i] != d {
match = false
break
}
}
if !match {
idx++
continue
}
if idx == 0 || idx == n-7 || light(idx-4, idx) || light(idx+7, idx+11) {
p += 40
idx += 7
} else {
idx += 4
}
}
return p
}