2017-01-16 03:14:29 +01:00
|
|
|
package qr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"image"
|
|
|
|
"image/color"
|
|
|
|
"math"
|
|
|
|
|
|
|
|
"github.com/boombuler/barcode"
|
|
|
|
"github.com/boombuler/barcode/utils"
|
|
|
|
)
|
|
|
|
|
|
|
|
type qrcode struct {
|
|
|
|
dimension int
|
|
|
|
data *utils.BitList
|
|
|
|
content string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) Content() string {
|
|
|
|
return qr.content
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) Metadata() barcode.Metadata {
|
2020-05-10 14:50:16 +02:00
|
|
|
return barcode.Metadata{barcode.TypeQR, 2}
|
2017-01-16 03:14:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) ColorModel() color.Model {
|
|
|
|
return color.Gray16Model
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) Bounds() image.Rectangle {
|
|
|
|
return image.Rect(0, 0, qr.dimension, qr.dimension)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) At(x, y int) color.Color {
|
|
|
|
if qr.Get(x, y) {
|
|
|
|
return color.Black
|
|
|
|
}
|
|
|
|
return color.White
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) Get(x, y int) bool {
|
|
|
|
return qr.data.GetBit(x*qr.dimension + y)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) Set(x, y int, val bool) {
|
|
|
|
qr.data.SetBit(x*qr.dimension+y, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) calcPenalty() uint {
|
|
|
|
return qr.calcPenaltyRule1() + qr.calcPenaltyRule2() + qr.calcPenaltyRule3() + qr.calcPenaltyRule4()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) calcPenaltyRule1() uint {
|
|
|
|
var result uint
|
|
|
|
for x := 0; x < qr.dimension; x++ {
|
|
|
|
checkForX := false
|
|
|
|
var cntX uint
|
|
|
|
checkForY := false
|
|
|
|
var cntY uint
|
|
|
|
|
|
|
|
for y := 0; y < qr.dimension; y++ {
|
|
|
|
if qr.Get(x, y) == checkForX {
|
|
|
|
cntX++
|
|
|
|
} else {
|
|
|
|
checkForX = !checkForX
|
|
|
|
if cntX >= 5 {
|
|
|
|
result += cntX - 2
|
|
|
|
}
|
|
|
|
cntX = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if qr.Get(y, x) == checkForY {
|
|
|
|
cntY++
|
|
|
|
} else {
|
|
|
|
checkForY = !checkForY
|
|
|
|
if cntY >= 5 {
|
|
|
|
result += cntY - 2
|
|
|
|
}
|
|
|
|
cntY = 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if cntX >= 5 {
|
|
|
|
result += cntX - 2
|
|
|
|
}
|
|
|
|
if cntY >= 5 {
|
|
|
|
result += cntY - 2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) calcPenaltyRule2() uint {
|
|
|
|
var result uint
|
|
|
|
for x := 0; x < qr.dimension-1; x++ {
|
|
|
|
for y := 0; y < qr.dimension-1; y++ {
|
|
|
|
check := qr.Get(x, y)
|
|
|
|
if qr.Get(x, y+1) == check && qr.Get(x+1, y) == check && qr.Get(x+1, y+1) == check {
|
|
|
|
result += 3
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) calcPenaltyRule3() uint {
|
|
|
|
pattern1 := []bool{true, false, true, true, true, false, true, false, false, false, false}
|
|
|
|
pattern2 := []bool{false, false, false, false, true, false, true, true, true, false, true}
|
|
|
|
|
|
|
|
var result uint
|
|
|
|
for x := 0; x <= qr.dimension-len(pattern1); x++ {
|
|
|
|
for y := 0; y < qr.dimension; y++ {
|
|
|
|
pattern1XFound := true
|
|
|
|
pattern2XFound := true
|
|
|
|
pattern1YFound := true
|
|
|
|
pattern2YFound := true
|
|
|
|
|
|
|
|
for i := 0; i < len(pattern1); i++ {
|
|
|
|
iv := qr.Get(x+i, y)
|
|
|
|
if iv != pattern1[i] {
|
|
|
|
pattern1XFound = false
|
|
|
|
}
|
|
|
|
if iv != pattern2[i] {
|
|
|
|
pattern2XFound = false
|
|
|
|
}
|
|
|
|
iv = qr.Get(y, x+i)
|
|
|
|
if iv != pattern1[i] {
|
|
|
|
pattern1YFound = false
|
|
|
|
}
|
|
|
|
if iv != pattern2[i] {
|
|
|
|
pattern2YFound = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if pattern1XFound || pattern2XFound {
|
|
|
|
result += 40
|
|
|
|
}
|
|
|
|
if pattern1YFound || pattern2YFound {
|
|
|
|
result += 40
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qr *qrcode) calcPenaltyRule4() uint {
|
|
|
|
totalNum := qr.data.Len()
|
|
|
|
trueCnt := 0
|
|
|
|
for i := 0; i < totalNum; i++ {
|
|
|
|
if qr.data.GetBit(i) {
|
|
|
|
trueCnt++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
percDark := float64(trueCnt) * 100 / float64(totalNum)
|
|
|
|
floor := math.Abs(math.Floor(percDark/5) - 10)
|
|
|
|
ceil := math.Abs(math.Ceil(percDark/5) - 10)
|
|
|
|
return uint(math.Min(floor, ceil) * 10)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newBarcode(dim int) *qrcode {
|
|
|
|
res := new(qrcode)
|
|
|
|
res.dimension = dim
|
|
|
|
res.data = utils.NewBitList(dim * dim)
|
|
|
|
return res
|
|
|
|
}
|