feature/ui - zatim ne uplne uhlazena ale celkem pouzitelna appka #1
2
go.mod
2
go.mod
@ -28,10 +28,12 @@ require (
|
|||||||
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
|
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
|
||||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea // indirect
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
|
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rymdport/portal v0.4.1 // indirect
|
github.com/rymdport/portal v0.4.1 // indirect
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||||
github.com/spf13/pflag v1.0.9 // indirect
|
github.com/spf13/pflag v1.0.9 // indirect
|
||||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||||
|
|||||||
8
go.sum
8
go.sum
@ -6,6 +6,7 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0
|
|||||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
|
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
|
||||||
@ -48,6 +49,8 @@ github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe9
|
|||||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea h1:uyJ13zfy6l79CM3HnVhDalIyZ4RJAyVfDrbnfFeJoC4=
|
||||||
|
github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea/go.mod h1:w4pGU9PkiX2hAWyF0yuHEHmYTQFAd6WHzp6+IY7JVjE=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
|
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
|
||||||
@ -61,6 +64,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
|
github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
|
||||||
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||||
@ -69,6 +74,8 @@ github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiY
|
|||||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||||
@ -92,5 +99,6 @@ golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
|||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
44
qr_support.go
Normal file
44
qr_support.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
|
||||||
|
"github.com/liyue201/goqr"
|
||||||
|
"github.com/skip2/go-qrcode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateQRPNG returns PNG bytes for the given text.
|
||||||
|
func GenerateQRPNG(text string, size int) ([]byte, error) {
|
||||||
|
if size <= 0 {
|
||||||
|
size = 256
|
||||||
|
}
|
||||||
|
png, err := qrcode.Encode(text, qrcode.Medium, size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return png, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeQR decodes first QR code text from an image.
|
||||||
|
func DecodeQR(img image.Image) (string, error) {
|
||||||
|
codes, err := goqr.Recognize(img)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(codes) == 0 {
|
||||||
|
return "", errors.New("no qr code found")
|
||||||
|
}
|
||||||
|
return string(codes[0].Payload), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPNG decodes raw PNG bytes to image.Image.
|
||||||
|
func LoadPNG(b []byte) (image.Image, error) {
|
||||||
|
im, err := png.Decode(bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return im, nil
|
||||||
|
}
|
||||||
324
ui.go
324
ui.go
@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
encrypt "fckeuspy-go/lib"
|
encrypt "fckeuspy-go/lib"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -11,30 +12,57 @@ import (
|
|||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/dialog"
|
"fyne.io/fyne/v2/dialog"
|
||||||
|
"fyne.io/fyne/v2/storage"
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
type uiParts struct {
|
type uiParts struct {
|
||||||
outKey *widget.Entry
|
outKey *widget.Entry
|
||||||
msg *widget.Entry
|
msg *widget.Entry
|
||||||
peer *widget.Entry
|
peer *widget.Entry
|
||||||
cipherOut *widget.Entry
|
cipherOut *widget.Entry
|
||||||
payload *widget.Entry
|
payload *widget.Entry
|
||||||
plainOut *widget.Entry
|
plainOut *widget.Entry
|
||||||
toastLabel *widget.Label
|
toastLabel *widget.Label
|
||||||
|
cipherQR *canvas.Image
|
||||||
|
pubQR *canvas.Image
|
||||||
|
crtQR *canvas.Image
|
||||||
|
showQR bool
|
||||||
|
peerQR *canvas.Image
|
||||||
|
showPeerQR bool
|
||||||
|
payloadQR *canvas.Image
|
||||||
|
showPayloadQR bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildEntries() *uiParts {
|
func buildEntries() *uiParts {
|
||||||
p := &uiParts{
|
p := &uiParts{
|
||||||
outKey: widget.NewMultiLineEntry(),
|
outKey: widget.NewMultiLineEntry(),
|
||||||
msg: widget.NewMultiLineEntry(),
|
msg: widget.NewMultiLineEntry(),
|
||||||
peer: widget.NewMultiLineEntry(),
|
peer: widget.NewMultiLineEntry(),
|
||||||
cipherOut: widget.NewMultiLineEntry(),
|
cipherOut: widget.NewMultiLineEntry(),
|
||||||
payload: widget.NewMultiLineEntry(),
|
payload: widget.NewMultiLineEntry(),
|
||||||
plainOut: widget.NewMultiLineEntry(),
|
plainOut: widget.NewMultiLineEntry(),
|
||||||
toastLabel: widget.NewLabel(""),
|
toastLabel: widget.NewLabel(""),
|
||||||
|
cipherQR: canvas.NewImageFromImage(nil),
|
||||||
|
pubQR: canvas.NewImageFromImage(nil),
|
||||||
|
crtQR: canvas.NewImageFromImage(nil),
|
||||||
|
showQR: true,
|
||||||
|
peerQR: canvas.NewImageFromImage(nil),
|
||||||
|
showPeerQR: true,
|
||||||
|
payloadQR: canvas.NewImageFromImage(nil),
|
||||||
|
showPayloadQR: true,
|
||||||
}
|
}
|
||||||
|
p.cipherQR.FillMode = canvas.ImageFillContain
|
||||||
|
p.cipherQR.SetMinSize(fyne.NewSize(260, 260))
|
||||||
|
p.pubQR.FillMode = canvas.ImageFillContain
|
||||||
|
p.pubQR.SetMinSize(fyne.NewSize(220, 220))
|
||||||
|
p.crtQR.FillMode = canvas.ImageFillContain
|
||||||
|
p.crtQR.SetMinSize(fyne.NewSize(220, 220))
|
||||||
|
p.peerQR.FillMode = canvas.ImageFillContain
|
||||||
|
p.peerQR.SetMinSize(fyne.NewSize(220, 220))
|
||||||
|
p.payloadQR.FillMode = canvas.ImageFillContain
|
||||||
|
p.payloadQR.SetMinSize(fyne.NewSize(260, 260))
|
||||||
p.outKey.SetPlaceHolder("Veřejný klíč / certifikát…")
|
p.outKey.SetPlaceHolder("Veřejný klíč / certifikát…")
|
||||||
p.msg.SetPlaceHolder("Sem napiš zprávu…")
|
p.msg.SetPlaceHolder("Sem napiš zprávu…")
|
||||||
p.peer.SetPlaceHolder("-----BEGIN PUBLIC KEY----- … nebo CERTIFICATE …")
|
p.peer.SetPlaceHolder("-----BEGIN PUBLIC KEY----- … nebo CERTIFICATE …")
|
||||||
@ -93,7 +121,6 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
|
|||||||
btnPaste := widget.NewButton("Paste", func() { parts.outKey.SetText(fyne.CurrentApp().Clipboard().Content()) })
|
btnPaste := widget.NewButton("Paste", func() { parts.outKey.SetText(fyne.CurrentApp().Clipboard().Content()) })
|
||||||
|
|
||||||
deleteBtn := widget.NewButton("Smazat identitu", func() {
|
deleteBtn := widget.NewButton("Smazat identitu", func() {
|
||||||
// dialog pro heslo
|
|
||||||
pwEntry := widget.NewPasswordEntry()
|
pwEntry := widget.NewPasswordEntry()
|
||||||
pwEntry.SetPlaceHolder("Heslo pro potvrzení…")
|
pwEntry.SetPlaceHolder("Heslo pro potvrzení…")
|
||||||
content := widget.NewForm(widget.NewFormItem("Heslo", pwEntry))
|
content := widget.NewForm(widget.NewFormItem("Heslo", pwEntry))
|
||||||
@ -114,16 +141,71 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
|
|||||||
dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
|
dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Quit app po odstranění (uživatel musí znovu spustit a vytvořit nový vault)
|
|
||||||
fyne.CurrentApp().Quit()
|
fyne.CurrentApp().Quit()
|
||||||
}, fyne.CurrentApp().Driver().AllWindows()[0])
|
}, fyne.CurrentApp().Driver().AllWindows()[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
tileIdentity := buttonTile(btnCopyPub, btnCopyCrt, btnPaste, btnShowPub, btnShowCrt, btnClear, deleteBtn)
|
makeQR := func(data string, target *canvas.Image) {
|
||||||
|
if data == "" {
|
||||||
|
target.Image = nil
|
||||||
|
target.Refresh()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pngBytes, err := GenerateQRPNG(data, 512)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
img, err := LoadPNG(pngBytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
target.Image = img
|
||||||
|
target.Refresh()
|
||||||
|
}
|
||||||
|
updateQRImages := func() {
|
||||||
|
if parts.showQR {
|
||||||
|
makeQR(svc.PublicPEM(), parts.pubQR)
|
||||||
|
makeQR(svc.PublicCert(), parts.crtQR)
|
||||||
|
} else {
|
||||||
|
parts.pubQR.Image = nil
|
||||||
|
parts.pubQR.Refresh()
|
||||||
|
parts.crtQR.Image = nil
|
||||||
|
parts.crtQR.Refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
identityContainer := container.NewVBox()
|
||||||
|
|
||||||
|
toggleBtn := widget.NewButton("", nil)
|
||||||
|
var rebuild func()
|
||||||
|
rebuild = func() {
|
||||||
|
identityContainer.Objects = nil
|
||||||
|
if parts.showQR {
|
||||||
|
updateQRImages()
|
||||||
|
// Wrap each QR with its copy button
|
||||||
|
pubBox := container.NewVBox(parts.pubQR, widget.NewButton("Copy", func() { copyClip(svc.PublicPEM(), parts) }))
|
||||||
|
crtBox := container.NewVBox(parts.crtQR, widget.NewButton("Copy", func() { copyClip(svc.PublicCert(), parts) }))
|
||||||
|
identityContainer.Add(container.NewGridWithColumns(2, pubBox, crtBox))
|
||||||
|
} else {
|
||||||
|
// show combined text for convenience
|
||||||
|
parts.outKey.SetText(svc.PublicPEM() + "\n" + svc.PublicCert())
|
||||||
|
identityContainer.Add(parts.outKey)
|
||||||
|
}
|
||||||
|
identityContainer.Refresh()
|
||||||
|
if parts.showQR {
|
||||||
|
toggleBtn.SetText("Zobrazit plaintext")
|
||||||
|
} else {
|
||||||
|
toggleBtn.SetText("Zobrazit QR")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toggleBtn.OnTapped = func() { parts.showQR = !parts.showQR; rebuild() }
|
||||||
|
rebuild()
|
||||||
|
|
||||||
|
tileIdentity := buttonTile(btnCopyPub, btnCopyCrt, btnPaste, btnShowPub, btnShowCrt, btnClear, deleteBtn, toggleBtn)
|
||||||
group := container.NewVBox(
|
group := container.NewVBox(
|
||||||
widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
tileIdentity,
|
tileIdentity,
|
||||||
parts.outKey,
|
identityContainer,
|
||||||
)
|
)
|
||||||
return container.NewVScroll(group)
|
return container.NewVScroll(group)
|
||||||
}
|
}
|
||||||
@ -148,11 +230,71 @@ func copyClip(s string, parts *uiParts) {
|
|||||||
// assembleResponsive builds split view that collapses for narrow widths
|
// assembleResponsive builds split view that collapses for narrow widths
|
||||||
// Tab: Encrypt
|
// Tab: Encrypt
|
||||||
func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||||
parts.cipherOut.Disable() // read only output
|
parts.cipherOut.Disable()
|
||||||
peerBtns := buttonTile(
|
peerBtns := buttonTile(
|
||||||
widget.NewButton("Paste", func() { parts.peer.SetText(fyne.CurrentApp().Clipboard().Content()) }),
|
widget.NewButton("Paste", func() { parts.peer.SetText(fyne.CurrentApp().Clipboard().Content()) }),
|
||||||
widget.NewButton("Clear", func() { parts.peer.SetText("") }),
|
widget.NewButton("Clear", func() { parts.peer.SetText("") }),
|
||||||
|
widget.NewButton("Copy", func() { copyClip(parts.peer.Text, parts) }),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
peerContainer := container.NewVBox()
|
||||||
|
peerToggle := widget.NewButton("", nil)
|
||||||
|
var updatePeer func()
|
||||||
|
updatePeerQR := func(text string) {
|
||||||
|
if text == "" {
|
||||||
|
parts.peerQR.Image = nil
|
||||||
|
parts.peerQR.Refresh()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pngBytes, err := GenerateQRPNG(text, 512)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
img, err := LoadPNG(pngBytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parts.peerQR.Image = img
|
||||||
|
parts.peerQR.Refresh()
|
||||||
|
}
|
||||||
|
updatePeer = func() {
|
||||||
|
peerContainer.Objects = nil
|
||||||
|
if parts.showPeerQR {
|
||||||
|
updatePeerQR(parts.peer.Text)
|
||||||
|
peerContainer.Add(container.NewVBox(parts.peerQR, widget.NewButton("Copy", func() { copyClip(parts.peer.Text, parts) })))
|
||||||
|
peerToggle.SetText("Zobrazit plaintext")
|
||||||
|
} else {
|
||||||
|
peerContainer.Add(parts.peer)
|
||||||
|
peerToggle.SetText("Zobrazit QR")
|
||||||
|
}
|
||||||
|
peerContainer.Refresh()
|
||||||
|
}
|
||||||
|
peerToggle.OnTapped = func() { parts.showPeerQR = !parts.showPeerQR; updatePeer() }
|
||||||
|
parts.peer.OnChanged = func(string) {
|
||||||
|
if parts.showPeerQR {
|
||||||
|
updatePeerQR(parts.peer.Text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updatePeer()
|
||||||
|
updateQR := func(text string) {
|
||||||
|
if text == "" {
|
||||||
|
parts.cipherQR.Image = nil
|
||||||
|
parts.cipherQR.Refresh()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pngBytes, err := GenerateQRPNG(text, 512)
|
||||||
|
if err != nil {
|
||||||
|
parts.showToast("QR error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
img, err := LoadPNG(pngBytes)
|
||||||
|
if err != nil {
|
||||||
|
parts.showToast("PNG err")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parts.cipherQR.Image = img
|
||||||
|
parts.cipherQR.Refresh()
|
||||||
|
}
|
||||||
encAction := func() {
|
encAction := func() {
|
||||||
m := parts.msg.Text
|
m := parts.msg.Text
|
||||||
p := parts.peer.Text
|
p := parts.peer.Text
|
||||||
@ -163,23 +305,74 @@ func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
|||||||
go func(msg, peer string) {
|
go func(msg, peer string) {
|
||||||
res, err := svc.Encrypt(msg, peer)
|
res, err := svc.Encrypt(msg, peer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fyne.Do(func() { parts.cipherOut.SetText(""); parts.showToast("Chyba") })
|
fyne.Do(func() { parts.cipherOut.SetText(""); updateQR(""); parts.showToast("Chyba") })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fyne.Do(func() { parts.cipherOut.SetText(res); parts.showToast("OK") })
|
fyne.Do(func() {
|
||||||
|
parts.cipherOut.SetText(res)
|
||||||
|
if parts.showQR {
|
||||||
|
updateQR(res)
|
||||||
|
}
|
||||||
|
parts.showToast("OK")
|
||||||
|
})
|
||||||
}(m, p)
|
}(m, p)
|
||||||
}
|
}
|
||||||
|
importPeerQR := func() {
|
||||||
|
fd := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
|
||||||
|
if err != nil || r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
data, _ := io.ReadAll(r)
|
||||||
|
img, err := LoadPNG(data)
|
||||||
|
if err != nil {
|
||||||
|
dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text, err := DecodeQR(img)
|
||||||
|
if err != nil {
|
||||||
|
dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parts.peer.SetText(text)
|
||||||
|
}, fyne.CurrentApp().Driver().AllWindows()[0])
|
||||||
|
fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"}))
|
||||||
|
fd.Show()
|
||||||
|
}
|
||||||
|
outputContainer := container.NewVBox()
|
||||||
|
toggleBtn := widget.NewButton("", nil)
|
||||||
|
var updateMode func()
|
||||||
|
updateMode = func() {
|
||||||
|
outputContainer.Objects = nil
|
||||||
|
if parts.showQR {
|
||||||
|
updateQR(parts.cipherOut.Text)
|
||||||
|
outputContainer.Add(container.NewVBox(parts.cipherQR, widget.NewButton("Copy", func() { copyClip(parts.cipherOut.Text, parts) })))
|
||||||
|
} else {
|
||||||
|
outputContainer.Add(parts.cipherOut)
|
||||||
|
}
|
||||||
|
if parts.showQR {
|
||||||
|
toggleBtn.SetText("Zobrazit plaintext")
|
||||||
|
} else {
|
||||||
|
toggleBtn.SetText("Zobrazit QR")
|
||||||
|
}
|
||||||
|
outputContainer.Refresh()
|
||||||
|
}
|
||||||
|
toggleBtn.OnTapped = func() { parts.showQR = !parts.showQR; updateMode() }
|
||||||
|
updateMode()
|
||||||
|
|
||||||
msgBtns := buttonTile(
|
msgBtns := buttonTile(
|
||||||
widget.NewButton("Clear+Paste", func() { parts.msg.SetText(""); parts.msg.SetText(fyne.CurrentApp().Clipboard().Content()) }),
|
widget.NewButton("Clear+Paste", func() { parts.msg.SetText(""); parts.msg.SetText(fyne.CurrentApp().Clipboard().Content()) }),
|
||||||
widget.NewButton("Encrypt", encAction),
|
widget.NewButton("Encrypt", func() { encAction(); updateMode() }),
|
||||||
widget.NewButton("Copy encrypted", func() { copyClip(parts.cipherOut.Text, parts) }),
|
widget.NewButton("Copy", func() { copyClip(parts.cipherOut.Text, parts) }),
|
||||||
|
widget.NewButton("Import Key QR", importPeerQR),
|
||||||
)
|
)
|
||||||
|
|
||||||
group := container.NewVBox(
|
group := container.NewVBox(
|
||||||
widget.NewLabelWithStyle("Šifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
widget.NewLabelWithStyle("Šifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
widget.NewLabel("Veřejný klíč příjemce"), peerBtns, parts.peer,
|
container.NewHBox(widget.NewLabel("Veřejný klíč příjemce"), peerToggle), peerBtns, peerContainer,
|
||||||
widget.NewLabel("Zpráva"), msgBtns, parts.msg,
|
widget.NewLabel("Zpráva"), msgBtns, parts.msg,
|
||||||
widget.NewLabel("Výsledek"), parts.cipherOut,
|
container.NewHBox(widget.NewLabel("Výstup"), toggleBtn),
|
||||||
|
outputContainer,
|
||||||
)
|
)
|
||||||
return container.NewVScroll(group)
|
return container.NewVScroll(group)
|
||||||
}
|
}
|
||||||
@ -202,19 +395,94 @@ func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
|||||||
fyne.Do(func() { parts.plainOut.SetText(res); parts.showToast("OK") })
|
fyne.Do(func() { parts.plainOut.SetText(res); parts.showToast("OK") })
|
||||||
}(pl)
|
}(pl)
|
||||||
}
|
}
|
||||||
|
updatePayloadQR := func(text string) {
|
||||||
|
if text == "" {
|
||||||
|
parts.payloadQR.Image = nil
|
||||||
|
parts.payloadQR.Refresh()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pngBytes, err := GenerateQRPNG(text, 512)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
img, err := LoadPNG(pngBytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parts.payloadQR.Image = img
|
||||||
|
parts.payloadQR.Refresh()
|
||||||
|
}
|
||||||
|
importPayloadQR := func() {
|
||||||
|
fd := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
|
||||||
|
if err != nil || r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
data, _ := io.ReadAll(r)
|
||||||
|
img, err := LoadPNG(data)
|
||||||
|
if err != nil {
|
||||||
|
dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text, err := DecodeQR(img)
|
||||||
|
if err != nil {
|
||||||
|
dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parts.payload.SetText(text)
|
||||||
|
if parts.showPayloadQR {
|
||||||
|
updatePayloadQR(text)
|
||||||
|
}
|
||||||
|
decryptAction()
|
||||||
|
}, fyne.CurrentApp().Driver().AllWindows()[0])
|
||||||
|
fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"}))
|
||||||
|
fd.Show()
|
||||||
|
}
|
||||||
payloadBtns := buttonTile(
|
payloadBtns := buttonTile(
|
||||||
widget.NewButton("Paste+Decrypt", func() {
|
widget.NewButton("Paste+Decrypt", func() {
|
||||||
clip := fyne.CurrentApp().Clipboard().Content()
|
clip := fyne.CurrentApp().Clipboard().Content()
|
||||||
parts.payload.SetText(clip)
|
parts.payload.SetText(clip)
|
||||||
|
if parts.showPayloadQR {
|
||||||
|
updatePayloadQR(clip)
|
||||||
|
}
|
||||||
decryptAction()
|
decryptAction()
|
||||||
}),
|
}),
|
||||||
// widget.NewButton("Decrypt", decryptAction),
|
widget.NewButton("Clear", func() {
|
||||||
widget.NewButton("Clear", func() { parts.payload.SetText(""); parts.plainOut.SetText("") }),
|
parts.payload.SetText("")
|
||||||
|
parts.plainOut.SetText("")
|
||||||
|
if parts.showPayloadQR {
|
||||||
|
updatePayloadQR("")
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
widget.NewButton("QR Import", importPayloadQR),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
payloadContainer := container.NewVBox()
|
||||||
|
payloadToggle := widget.NewButton("", nil)
|
||||||
|
var updateMode func()
|
||||||
|
updateMode = func() {
|
||||||
|
payloadContainer.Objects = nil
|
||||||
|
if parts.showPayloadQR {
|
||||||
|
updatePayloadQR(parts.payload.Text)
|
||||||
|
payloadContainer.Add(container.NewVBox(parts.payloadQR, widget.NewButton("Copy", func() { copyClip(parts.payload.Text, parts) })))
|
||||||
|
payloadToggle.SetText("Zobrazit plaintext")
|
||||||
|
} else {
|
||||||
|
payloadContainer.Add(parts.payload)
|
||||||
|
payloadToggle.SetText("Zobrazit QR")
|
||||||
|
}
|
||||||
|
payloadContainer.Refresh()
|
||||||
|
}
|
||||||
|
payloadToggle.OnTapped = func() { parts.showPayloadQR = !parts.showPayloadQR; updateMode() }
|
||||||
|
parts.payload.OnChanged = func(string) {
|
||||||
|
if parts.showPayloadQR {
|
||||||
|
updatePayloadQR(parts.payload.Text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateMode()
|
||||||
plainBtns := buttonTile(widget.NewButton("Copy", func() { copyClip(parts.plainOut.Text, parts) }))
|
plainBtns := buttonTile(widget.NewButton("Copy", func() { copyClip(parts.plainOut.Text, parts) }))
|
||||||
group := container.NewVBox(
|
group := container.NewVBox(
|
||||||
widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
widget.NewLabel("Payload"), payloadBtns, parts.payload,
|
container.NewHBox(widget.NewLabel("Payload"), payloadToggle), payloadBtns, payloadContainer,
|
||||||
widget.NewLabel("Výsledek"), plainBtns, parts.plainOut,
|
widget.NewLabel("Výsledek"), plainBtns, parts.plainOut,
|
||||||
)
|
)
|
||||||
return container.NewVScroll(group)
|
return container.NewVScroll(group)
|
||||||
|
|||||||
23
vendor/github.com/liyue201/goqr/.travis.yml
generated
vendored
Normal file
23
vendor/github.com/liyue201/goqr/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
recipients:
|
||||||
|
- liyue201@126.com
|
||||||
|
on_success: change
|
||||||
|
on_failure: always
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.12
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test ./... -v -covermode=count -coverprofile=coverage.out
|
||||||
|
- $GOPATH/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- GO111MODULE=on
|
||||||
|
- secure: "aOzEdb/ST82OpO4LqLpwrdRs6GnkmdLJbm3o9+2HmJCyiHkhqPY1bGY1TiYxQLl3GkjFkuVP+3RB+E0p2R/1f4xYkg4cIpPoLm+4xJ7c1NsEGTaWVS8rCNTicqdym7C9kJart6KcAeWWoElFxob3+lTdS6sz4kFaURrVQ/lpbzBESH0B5Rwa35u1WasmHkPxsHJ/2a7Xnknv+SerjgMUDiXUYM5IjUWt2fV2Zq/9blTSReng6+6Lj9EdziZcQfWNJxBK/NDBtpNIpOdY+bZEUDQi7InB3IioKpQeVjMGKSAzqhedqnGbZsaO734Z8VjRjcFRJPuuYMMkMw4hGoplDqqrTa2aFgln54NiNk2R6WIcpPKAFdLEL7rCPx6qRLMRq1EEn6h+lAy5mJ6NIpmJmY/IbDEVH9D4z4YVLJbHhU+yAN4Tk5mwW9N2bLceSNxIRSJcCcDlRiBNba5XLdNb/tHq7PEM2lX1oXp4WzdYgGh08a69OAweY/CRQjiTZrbhmbsZ5qo3UMLA34HTfEVWt8UWeAFry5fhtzeLqQa5h0BPwtFsy9bsdukEL3nAWcWF4HaQ2jh2CNO6OvpzRb8i45qj1QpIvGqy/lJbuO5WswFQaZcKK2nmPa2M4f7+EBGFe3VvmOJcJvAt2ZRYVZalbdTLDKMmMgcdYIL4jzzcSs4="
|
||||||
165
vendor/github.com/liyue201/goqr/LICENSE
generated
vendored
Normal file
165
vendor/github.com/liyue201/goqr/LICENSE
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
54
vendor/github.com/liyue201/goqr/README.md
generated
vendored
Normal file
54
vendor/github.com/liyue201/goqr/README.md
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# goqr
|
||||||
|
[](https://godoc.org/github.com/liyue201/goqr)
|
||||||
|
[](https://goreportcard.com/report/github.com/liyue201/goqr)
|
||||||
|
[](https://travis-ci.org/liyue201/goqr)
|
||||||
|
[](https://coveralls.io/github/liyue201/goqr)
|
||||||
|
[](/LICENSE)
|
||||||
|
[](/example)
|
||||||
|
|
||||||
|
|
||||||
|
This is a QR Code recognition and decoding library in pure go. It can recognize most of images into QR Code string.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/liyue201/goqr"
|
||||||
|
"image"
|
||||||
|
_ "image/jpeg"
|
||||||
|
_ "image/png"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func recognizeFile(path string) {
|
||||||
|
fmt.Printf("recognize file: %v\n", path)
|
||||||
|
imgdata, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
img, _, err := image.Decode(bytes.NewReader(imgdata))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("image.Decode error: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
qrCodes, err := goqr.Recognize(img)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Recognize failed: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, qrCode := range qrCodes {
|
||||||
|
fmt.Printf("qrCode text: %s\n", qrCode.Payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
recognizeFile("testdata/008.png")
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
716
vendor/github.com/liyue201/goqr/decoding.go
generated
vendored
Normal file
716
vendor/github.com/liyue201/goqr/decoding.go
generated
vendored
Normal file
@ -0,0 +1,716 @@
|
|||||||
|
package goqr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxPoly = 64
|
||||||
|
|
||||||
|
type galoisField struct {
|
||||||
|
p int
|
||||||
|
log []uint8
|
||||||
|
exp []uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
var gf16Exp = []uint8{
|
||||||
|
0x01, 0x02, 0x04, 0x08, 0x03, 0x06, 0x0c, 0x0b,
|
||||||
|
0x05, 0x0a, 0x07, 0x0e, 0x0f, 0x0d, 0x09, 0x01,
|
||||||
|
}
|
||||||
|
|
||||||
|
var gf16Log = []uint8{
|
||||||
|
0x00, 0x0f, 0x01, 0x04, 0x02, 0x08, 0x05, 0x0a,
|
||||||
|
0x03, 0x0e, 0x09, 0x07, 0x06, 0x0d, 0x0b, 0x0c,
|
||||||
|
}
|
||||||
|
|
||||||
|
var gf16 = galoisField{
|
||||||
|
p: 15,
|
||||||
|
log: gf16Log,
|
||||||
|
exp: gf16Exp,
|
||||||
|
}
|
||||||
|
|
||||||
|
var gf256Exp = [256]uint8{
|
||||||
|
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
|
||||||
|
0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
|
||||||
|
0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9,
|
||||||
|
0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
|
||||||
|
0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35,
|
||||||
|
0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
|
||||||
|
0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0,
|
||||||
|
0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
|
||||||
|
0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc,
|
||||||
|
0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
|
||||||
|
0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f,
|
||||||
|
0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
|
||||||
|
0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88,
|
||||||
|
0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
|
||||||
|
0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93,
|
||||||
|
0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
|
||||||
|
0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9,
|
||||||
|
0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
|
||||||
|
0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa,
|
||||||
|
0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
|
||||||
|
0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e,
|
||||||
|
0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
|
||||||
|
0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4,
|
||||||
|
0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
|
||||||
|
0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e,
|
||||||
|
0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
|
||||||
|
0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef,
|
||||||
|
0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
|
||||||
|
0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5,
|
||||||
|
0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
|
||||||
|
0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83,
|
||||||
|
0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01,
|
||||||
|
}
|
||||||
|
|
||||||
|
var gf256Log = [256]uint8{
|
||||||
|
0x00, 0xff, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6,
|
||||||
|
0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
|
||||||
|
0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81,
|
||||||
|
0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
|
||||||
|
0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21,
|
||||||
|
0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
|
||||||
|
0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9,
|
||||||
|
0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
|
||||||
|
0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd,
|
||||||
|
0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
|
||||||
|
0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd,
|
||||||
|
0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
|
||||||
|
0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e,
|
||||||
|
0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
|
||||||
|
0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b,
|
||||||
|
0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
|
||||||
|
0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d,
|
||||||
|
0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
|
||||||
|
0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c,
|
||||||
|
0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
|
||||||
|
0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd,
|
||||||
|
0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
|
||||||
|
0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e,
|
||||||
|
0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
|
||||||
|
0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76,
|
||||||
|
0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
|
||||||
|
0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa,
|
||||||
|
0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
|
||||||
|
0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51,
|
||||||
|
0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
|
||||||
|
0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8,
|
||||||
|
0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf,
|
||||||
|
}
|
||||||
|
|
||||||
|
var gf256 = galoisField{
|
||||||
|
255,
|
||||||
|
gf256Log[:],
|
||||||
|
gf256Exp[:],
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Polynomial operations
|
||||||
|
*/
|
||||||
|
|
||||||
|
func polyAdd(dst, src []uint8, c uint8, shift int, gf *galoisField) {
|
||||||
|
|
||||||
|
logC := gf.log[c]
|
||||||
|
if c == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < maxPoly; i++ {
|
||||||
|
p := i + shift
|
||||||
|
v := src[i]
|
||||||
|
if p < 0 || p >= maxPoly {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pos := (int(gf.log[v]) + int(logC)) % gf.p
|
||||||
|
|
||||||
|
dst[p] ^= gf.exp[pos]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func polyEval(s []uint8, x uint8, gf *galoisField) uint8 {
|
||||||
|
sum := uint8(0)
|
||||||
|
logX := gf.log[x]
|
||||||
|
|
||||||
|
if x == 0 {
|
||||||
|
return s[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < maxPoly; i++ {
|
||||||
|
c := s[i]
|
||||||
|
if c == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sum ^= gf.exp[(int(gf.log[c])+int(logX)*i)%gf.p]
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Berlekamp-Massey algorithm for finding error locator polynomials.
|
||||||
|
*/
|
||||||
|
|
||||||
|
func berlekampMassey(s []uint8, num int, gf *galoisField, sigma []uint8) {
|
||||||
|
|
||||||
|
C := make([]uint8, maxPoly)
|
||||||
|
B := make([]uint8, maxPoly)
|
||||||
|
|
||||||
|
L := 0
|
||||||
|
m := 1
|
||||||
|
b := uint8(1)
|
||||||
|
|
||||||
|
B[0] = 1
|
||||||
|
C[0] = 1
|
||||||
|
|
||||||
|
for n := 0; n < num; n++ {
|
||||||
|
d := s[n]
|
||||||
|
|
||||||
|
for i := 1; i <= L; i++ {
|
||||||
|
if !(C[i] > 0 && s[n-i] > 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d ^= gf.exp[(int(gf.log[C[i]])+int(gf.log[s[n-i]]))%gf.p]
|
||||||
|
}
|
||||||
|
|
||||||
|
mult := gf.exp[(gf.p-int(gf.log[b])+int(gf.log[d]))%gf.p]
|
||||||
|
|
||||||
|
if d == 0 {
|
||||||
|
m++
|
||||||
|
} else if L*2 <= n {
|
||||||
|
T := make([]uint8, 0)
|
||||||
|
T = append(T, C...)
|
||||||
|
|
||||||
|
polyAdd(C, B, mult, m, gf)
|
||||||
|
|
||||||
|
B = B[:0]
|
||||||
|
B = append(B, T...)
|
||||||
|
|
||||||
|
L = n + 1 - L
|
||||||
|
b = d
|
||||||
|
m = 1
|
||||||
|
|
||||||
|
} else {
|
||||||
|
polyAdd(C, B, mult, m, gf)
|
||||||
|
m++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sigma = sigma[:0]
|
||||||
|
sigma = append(sigma, C...)
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Code stream error correction
|
||||||
|
*
|
||||||
|
* Generator polynomial for GF(2^8) is x^8 + x^4 + x^3 + x^2 + 1
|
||||||
|
*/
|
||||||
|
|
||||||
|
func blockSyndromes(data []uint8, bs, npar int, s []uint8) int {
|
||||||
|
nonzero := 0
|
||||||
|
for i := 0; i < maxPoly; i++ {
|
||||||
|
s[i] = 0
|
||||||
|
}
|
||||||
|
for i := 0; i < npar; i++ {
|
||||||
|
for j := 0; j < bs; j++ {
|
||||||
|
c := data[bs-j-1]
|
||||||
|
if c == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s[i] ^= gf256Exp[((int)(gf256Log[c])+i*j)%255]
|
||||||
|
}
|
||||||
|
if s[i] != 0 {
|
||||||
|
nonzero = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nonzero
|
||||||
|
}
|
||||||
|
|
||||||
|
func elocPoly(omega []uint8, s []uint8, sigma []uint8, npar int) {
|
||||||
|
|
||||||
|
for i := 0; i < maxPoly; i++ {
|
||||||
|
omega[i] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < npar; i++ {
|
||||||
|
a := sigma[i]
|
||||||
|
logA := gf256Log[a]
|
||||||
|
|
||||||
|
if a == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j+1 < maxPoly; j++ {
|
||||||
|
b := s[j+1]
|
||||||
|
if i+j >= npar {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if b == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
omega[i+j] ^= gf256Exp[(int(logA)+int(gf256Log[b]))%255]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func correctBlock(data []uint8, ecc *qrRsParams) error {
|
||||||
|
|
||||||
|
npar := ecc.bs - ecc.dw
|
||||||
|
s := make([]uint8, maxPoly)
|
||||||
|
|
||||||
|
// Compute syndrome vector
|
||||||
|
if 0 == blockSyndromes(data, ecc.bs, npar, s) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sigma := make([]uint8, maxPoly)
|
||||||
|
berlekampMassey(s, npar, &gf256, sigma)
|
||||||
|
|
||||||
|
// Compute derivative of sigma
|
||||||
|
sigmaDeriv := make([]uint8, maxPoly)
|
||||||
|
for i := 0; i+1 < maxPoly; i += 2 {
|
||||||
|
sigmaDeriv[i] = sigma[i+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute error evaluator polynomial
|
||||||
|
omega := make([]uint8, maxPoly)
|
||||||
|
elocPoly(omega, s, sigma, npar-1)
|
||||||
|
|
||||||
|
// Find error locations and magnitudes
|
||||||
|
for i := 0; i < ecc.bs; i++ {
|
||||||
|
xinv := gf256Exp[255-i]
|
||||||
|
if 0 == polyEval(sigma, xinv, &gf256) {
|
||||||
|
sdX := polyEval(sigmaDeriv, xinv, &gf256)
|
||||||
|
omegaX := polyEval(omega, xinv, &gf256)
|
||||||
|
error := gf256Exp[(255-int(gf256Log[sdX])+int(gf256Log[omegaX]))%255]
|
||||||
|
data[ecc.bs-i-1] ^= error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockSyndromes(data, ecc.bs, npar, s) != 0 {
|
||||||
|
return ErrDataEcc
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Format value error correction
|
||||||
|
*
|
||||||
|
* Generator polynomial for GF(2^4) is x^4 + x + 1
|
||||||
|
*/
|
||||||
|
|
||||||
|
const formatMaxError = 3
|
||||||
|
const formatSyndrome = formatMaxError * 2
|
||||||
|
const formatBits = 15
|
||||||
|
|
||||||
|
func formatSyndromes(u uint16, s []uint8) int {
|
||||||
|
nonzero := 0
|
||||||
|
for i := 0; i < maxPoly; i++ {
|
||||||
|
s[i] = 0
|
||||||
|
}
|
||||||
|
for i := 0; i < formatSyndrome; i++ {
|
||||||
|
s[i] = 0
|
||||||
|
for j := 0; j < formatBits; j++ {
|
||||||
|
if u&(uint16(1 << uint(j))) != 0 {
|
||||||
|
s[i] ^= gf16Exp[((i+1)*j)%15]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s[i] != 0 {
|
||||||
|
nonzero = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nonzero
|
||||||
|
}
|
||||||
|
|
||||||
|
func correctFormat(fRet *uint16) error {
|
||||||
|
u := *fRet
|
||||||
|
|
||||||
|
// Evaluate U (received codeword) at each of alpha_1 .. alpha_6
|
||||||
|
// to get S_1 .. S_6 (but we index them from 0).
|
||||||
|
|
||||||
|
s := make([]uint8, maxPoly)
|
||||||
|
if 0 == formatSyndromes(u, s) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sigma := make([]uint8, maxPoly)
|
||||||
|
berlekampMassey(s, formatSyndrome, &gf16, sigma)
|
||||||
|
|
||||||
|
// Now, find the roots of the polynomial
|
||||||
|
for i := 0; i < 15; i++ {
|
||||||
|
if 0 == polyEval(sigma, gf16Exp[15-i], &gf16) {
|
||||||
|
u ^= 1 << uint16(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if 0 != formatSyndromes(u, s) {
|
||||||
|
return ErrFormatEcc
|
||||||
|
}
|
||||||
|
|
||||||
|
*fRet = u
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Decoder algorithm
|
||||||
|
*/
|
||||||
|
|
||||||
|
type datastream struct {
|
||||||
|
raw [qrMaxPayload]uint8
|
||||||
|
dataBits int
|
||||||
|
ptr int
|
||||||
|
data [qrMaxPayload]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func maskBit(mask, i, j int) int {
|
||||||
|
k := 0
|
||||||
|
switch mask {
|
||||||
|
case 0:
|
||||||
|
k = (i + j) % 2
|
||||||
|
case 1:
|
||||||
|
k = i % 2
|
||||||
|
case 2:
|
||||||
|
k = j % 3
|
||||||
|
case 3:
|
||||||
|
k = (i + j) % 3
|
||||||
|
case 4:
|
||||||
|
k = ((i / 2) + (j / 3)) % 2
|
||||||
|
case 5:
|
||||||
|
k = (i*j)%2 + (i*j)%3
|
||||||
|
case 6:
|
||||||
|
k = ((i*j)%2 + (i*j)%3) % 2
|
||||||
|
case 7:
|
||||||
|
k = ((i*j)%3 + (i+j)%2) % 2
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if k != 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func reservedCell(version, i, j int) int {
|
||||||
|
ver := &qrVersionDb[version]
|
||||||
|
size := version*4 + 17
|
||||||
|
ai := -1
|
||||||
|
aj := -1
|
||||||
|
a := 0
|
||||||
|
|
||||||
|
// Finder + format: top left
|
||||||
|
if i < 9 && j < 9 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finder + format: bottom left
|
||||||
|
if i+8 >= size && j < 9 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finder + format: top right
|
||||||
|
if i < 9 && j+8 >= size {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
// Exclude timing patterns
|
||||||
|
if i == 6 || j == 6 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude Version info, if it exists. Version info sits adjacent to
|
||||||
|
// the top-right and bottom-left finders in three rows, bounded by
|
||||||
|
// the timing pattern.
|
||||||
|
|
||||||
|
if version >= 7 {
|
||||||
|
if i < 6 && j+11 >= size {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+11 >= size && j < 6 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Exclude alignment patterns
|
||||||
|
for a = 0; a < qrMaxAliment && ver.apat[a] != 0; a++ {
|
||||||
|
p := ver.apat[a]
|
||||||
|
|
||||||
|
if int(math.Abs(float64(p-i))) < 3 {
|
||||||
|
ai = a
|
||||||
|
}
|
||||||
|
if int(math.Abs(float64(p-j))) < 3 {
|
||||||
|
aj = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ai >= 0 && aj >= 0 {
|
||||||
|
a--
|
||||||
|
if ai > 0 && ai < a {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if aj > 0 && aj < a {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if aj == a && ai == a {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func codestreamEcc(data *QRData, ds *datastream) error {
|
||||||
|
ver := &qrVersionDb[data.Version]
|
||||||
|
sbEcc := &ver.ecc[data.EccLevel]
|
||||||
|
var lbEcc qrRsParams
|
||||||
|
lbCount := (ver.dataBytes - sbEcc.bs*sbEcc.ns) / (sbEcc.bs + 1)
|
||||||
|
|
||||||
|
bc := lbCount + sbEcc.ns
|
||||||
|
eccOffset := sbEcc.dw*bc + lbCount
|
||||||
|
|
||||||
|
dstOffset := 0
|
||||||
|
lbEcc = *sbEcc
|
||||||
|
|
||||||
|
lbEcc.dw++
|
||||||
|
lbEcc.bs++
|
||||||
|
|
||||||
|
for i := 0; i < bc; i++ {
|
||||||
|
dst := ds.data[dstOffset:]
|
||||||
|
ecc := sbEcc
|
||||||
|
if i < sbEcc.ns {
|
||||||
|
ecc = sbEcc
|
||||||
|
} else {
|
||||||
|
ecc = &lbEcc
|
||||||
|
}
|
||||||
|
numEc := ecc.bs - ecc.dw
|
||||||
|
|
||||||
|
for j := 0; j < ecc.dw; j++ {
|
||||||
|
dst[j] = ds.raw[j*bc+i]
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < numEc; j++ {
|
||||||
|
dst[ecc.dw+j] = ds.raw[eccOffset+j*bc+i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err := correctBlock(dst, ecc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dstOffset += ecc.dw
|
||||||
|
}
|
||||||
|
|
||||||
|
ds.dataBits = dstOffset * 8
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bitsRemaining(ds *datastream) int {
|
||||||
|
return ds.dataBits - ds.ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func takeBits(ds *datastream, len int) int {
|
||||||
|
ret := 0
|
||||||
|
for len > 0 && (ds.ptr < ds.dataBits) {
|
||||||
|
b := ds.data[ds.ptr>>3]
|
||||||
|
bitpos := ds.ptr & 7
|
||||||
|
ret <<= 1
|
||||||
|
if ((b << uint(bitpos)) & 0x80) != 0 {
|
||||||
|
ret |= 1
|
||||||
|
}
|
||||||
|
ds.ptr++
|
||||||
|
len--
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func numericTuple(data *QRData, ds *datastream, bits, digits int) int {
|
||||||
|
if bitsRemaining(ds) < bits {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
tuple := takeBits(ds, bits)
|
||||||
|
|
||||||
|
for i := digits - 1; i >= 0; i-- {
|
||||||
|
data.Payload = append(data.Payload, uint8(tuple%10)+uint8('0'))
|
||||||
|
|
||||||
|
tuple /= 10
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeNumeric(data *QRData, ds *datastream) error {
|
||||||
|
bits := 14
|
||||||
|
if data.Version < 10 {
|
||||||
|
bits = 10
|
||||||
|
} else if data.Version < 27 {
|
||||||
|
bits = 12
|
||||||
|
}
|
||||||
|
|
||||||
|
count := takeBits(ds, bits)
|
||||||
|
if len(data.Payload)+count+1 > qrMaxPayload {
|
||||||
|
return ErrDataOverflow
|
||||||
|
}
|
||||||
|
for count >= 3 {
|
||||||
|
if numericTuple(data, ds, 10, 3) < 0 {
|
||||||
|
return ErrDataUnderflow
|
||||||
|
}
|
||||||
|
count -= 3
|
||||||
|
}
|
||||||
|
if count >= 2 {
|
||||||
|
if numericTuple(data, ds, 7, 2) < 0 {
|
||||||
|
return ErrDataUnderflow
|
||||||
|
|
||||||
|
}
|
||||||
|
count -= 2
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
if numericTuple(data, ds, 4, 1) < 0 {
|
||||||
|
return ErrDataUnderflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var alphaMap = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")
|
||||||
|
|
||||||
|
func alphaTuple(data *QRData, ds *datastream, bits, digits int) int {
|
||||||
|
if bitsRemaining(ds) < bits {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
tuple := takeBits(ds, bits)
|
||||||
|
payloadLen := len(data.Payload)
|
||||||
|
data.Payload = append(data.Payload, make([]uint8, digits)...)
|
||||||
|
for i := 0; i < digits; i++ {
|
||||||
|
data.Payload[payloadLen+digits-i-1] = alphaMap[tuple%45]
|
||||||
|
tuple /= 45
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeAlpha(data *QRData, ds *datastream) error {
|
||||||
|
bits := 13
|
||||||
|
if data.Version < 10 {
|
||||||
|
bits = 9
|
||||||
|
} else if data.Version < 27 {
|
||||||
|
bits = 11
|
||||||
|
}
|
||||||
|
count := takeBits(ds, bits)
|
||||||
|
if len(data.Payload)+count+1 > qrMaxPayload {
|
||||||
|
return ErrDataOverflow
|
||||||
|
}
|
||||||
|
for count >= 2 {
|
||||||
|
if alphaTuple(data, ds, 11, 2) < 0 {
|
||||||
|
return ErrDataUnderflow
|
||||||
|
}
|
||||||
|
count -= 2
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
if alphaTuple(data, ds, 6, 1) < 0 {
|
||||||
|
return ErrDataUnderflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeByte(data *QRData, ds *datastream) error {
|
||||||
|
bits := 16
|
||||||
|
if data.Version < 10 {
|
||||||
|
bits = 8
|
||||||
|
}
|
||||||
|
count := takeBits(ds, bits)
|
||||||
|
if len(data.Payload)+count+1 > qrMaxPayload {
|
||||||
|
return ErrDataOverflow
|
||||||
|
}
|
||||||
|
if bitsRemaining(ds) < count*8 {
|
||||||
|
return ErrDataUnderflow
|
||||||
|
}
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
data.Payload = append(data.Payload, uint8(takeBits(ds, 8)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeKanji(data *QRData, ds *datastream) error {
|
||||||
|
bits := 12
|
||||||
|
|
||||||
|
if data.Version < 10 {
|
||||||
|
bits = 8
|
||||||
|
} else if data.Version < 27 {
|
||||||
|
bits = 10
|
||||||
|
}
|
||||||
|
count := takeBits(ds, bits)
|
||||||
|
|
||||||
|
if len(data.Payload)+count*2+1 > qrMaxPayload {
|
||||||
|
return ErrDataOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
if bitsRemaining(ds) < count*13 {
|
||||||
|
return ErrDataUnderflow
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
d := takeBits(ds, 13)
|
||||||
|
msB := d / 0xc0
|
||||||
|
lsB := d % 0xc0
|
||||||
|
intermediate := uint16((msB << 8) | lsB)
|
||||||
|
var sjw uint16
|
||||||
|
|
||||||
|
if intermediate+0x8140 <= 0x9ffc {
|
||||||
|
// bytes are in the range 0x8140 to 0x9FFC
|
||||||
|
sjw = intermediate + 0x8140
|
||||||
|
} else {
|
||||||
|
// bytes are in the range 0xE040 to 0xEBBF
|
||||||
|
sjw = intermediate + 0xc140
|
||||||
|
}
|
||||||
|
data.Payload = append(data.Payload, uint8(sjw>>8))
|
||||||
|
data.Payload = append(data.Payload, uint8(sjw&0xff))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeEci(data *QRData, ds *datastream) error {
|
||||||
|
if bitsRemaining(ds) < 8 {
|
||||||
|
return ErrDataOverflow
|
||||||
|
}
|
||||||
|
data.Eci = uint32(takeBits(ds, 8))
|
||||||
|
if (data.Eci & 0xc0) == 0x80 {
|
||||||
|
if bitsRemaining(ds) < 8 {
|
||||||
|
return ErrDataUnderflow
|
||||||
|
}
|
||||||
|
data.Eci = (data.Eci << 8) | uint32(takeBits(ds, 8))
|
||||||
|
} else if (data.Eci & 0xe0) == 0xc0 {
|
||||||
|
if bitsRemaining(ds) < 16 {
|
||||||
|
return ErrDataUnderflow
|
||||||
|
}
|
||||||
|
data.Eci = (data.Eci << 16) | uint32(takeBits(ds, 16))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePayload(data *QRData, ds *datastream) error {
|
||||||
|
|
||||||
|
for bitsRemaining(ds) >= 4 {
|
||||||
|
var err error
|
||||||
|
_type := takeBits(ds, 4)
|
||||||
|
switch _type {
|
||||||
|
case qrDataTypeNumeric:
|
||||||
|
err = decodeNumeric(data, ds)
|
||||||
|
case qrDataTypeAlpha:
|
||||||
|
err = decodeAlpha(data, ds)
|
||||||
|
case qrDataTypeByte:
|
||||||
|
err = decodeByte(data, ds)
|
||||||
|
case qrDataTypeKanji:
|
||||||
|
err = decodeKanji(data, ds)
|
||||||
|
case 7:
|
||||||
|
err = decodeEci(data, ds)
|
||||||
|
default:
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if 0 == (_type&(_type-1)) && (_type > data.DataType) {
|
||||||
|
data.DataType = _type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
83
vendor/github.com/liyue201/goqr/define.go
generated
vendored
Normal file
83
vendor/github.com/liyue201/goqr/define.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package goqr
|
||||||
|
|
||||||
|
const (
|
||||||
|
qrPixelWhite = 0
|
||||||
|
qrPixelBlack = 1
|
||||||
|
qrPixelRegion = 2
|
||||||
|
qrMaxRegion = 254
|
||||||
|
qrMaxCastones = 32
|
||||||
|
qrMaxGrids = 8
|
||||||
|
qrPerspectiveParams = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
type qrPixelType = uint8
|
||||||
|
|
||||||
|
type point struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
type qrRegion struct {
|
||||||
|
seed point
|
||||||
|
count int
|
||||||
|
capstone int
|
||||||
|
}
|
||||||
|
|
||||||
|
type qrCapstone struct {
|
||||||
|
ring int
|
||||||
|
stone int
|
||||||
|
corners [4]point
|
||||||
|
center point
|
||||||
|
c [qrPerspectiveParams]float64
|
||||||
|
qrGrid int
|
||||||
|
}
|
||||||
|
|
||||||
|
type qrGrid struct {
|
||||||
|
// Capstone indices
|
||||||
|
caps [3]int
|
||||||
|
|
||||||
|
// Alignment pattern region and corner
|
||||||
|
alignRegion int
|
||||||
|
align point
|
||||||
|
|
||||||
|
// Timing pattern endpoints
|
||||||
|
tpep [3]point
|
||||||
|
hscan int
|
||||||
|
vscan int
|
||||||
|
|
||||||
|
// Grid size and perspective transform
|
||||||
|
gridSize int
|
||||||
|
c [qrPerspectiveParams]float64
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* QR-code Version information database
|
||||||
|
*/
|
||||||
|
|
||||||
|
const (
|
||||||
|
qrMaxVersion = 40
|
||||||
|
qrMaxAliment = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
type qrRsParams struct {
|
||||||
|
bs int // Small block size
|
||||||
|
dw int // Small data words
|
||||||
|
ns int // Number of small blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
type qrVersionInfo struct {
|
||||||
|
dataBytes int
|
||||||
|
apat [qrMaxAliment]int
|
||||||
|
ecc [4]qrRsParams
|
||||||
|
}
|
||||||
|
|
||||||
|
type polygonScoreData struct {
|
||||||
|
ref point
|
||||||
|
scores [4]int
|
||||||
|
corners []point
|
||||||
|
}
|
||||||
|
|
||||||
|
type neighbour struct {
|
||||||
|
index int
|
||||||
|
distance float64
|
||||||
|
}
|
||||||
15
vendor/github.com/liyue201/goqr/errors.go
generated
vendored
Normal file
15
vendor/github.com/liyue201/goqr/errors.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package goqr
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// Error definition
|
||||||
|
var (
|
||||||
|
ErrNoQRCode = errors.New("no QR code in image")
|
||||||
|
ErrInvalidGridSize = errors.New("invalid grid size")
|
||||||
|
ErrInvalidVersion = errors.New("invalid version")
|
||||||
|
ErrFormatEcc = errors.New("ecc format error")
|
||||||
|
ErrDataEcc = errors.New("ecc data error")
|
||||||
|
ErrUnknownDataType = errors.New("unknown data type")
|
||||||
|
ErrDataOverflow = errors.New("data overflow")
|
||||||
|
ErrDataUnderflow = errors.New("data underflow")
|
||||||
|
)
|
||||||
36
vendor/github.com/liyue201/goqr/qr_const.go
generated
vendored
Normal file
36
vendor/github.com/liyue201/goqr/qr_const.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package goqr
|
||||||
|
|
||||||
|
// Limits on the maximum size of QR-codes and their content
|
||||||
|
const (
|
||||||
|
qrMaxBimap = 3917
|
||||||
|
qrMaxPayload = 8896
|
||||||
|
|
||||||
|
// QR-code ECC types
|
||||||
|
qrEccLevelM = 0
|
||||||
|
qrEccLevelL = 1
|
||||||
|
qrEccLevelH = 2
|
||||||
|
qrEccLevelQ = 3
|
||||||
|
|
||||||
|
// QR-code data types
|
||||||
|
qrDataTypeNumeric = 1
|
||||||
|
qrDataTypeAlpha = 2
|
||||||
|
qrDataTypeByte = 4
|
||||||
|
qrDataTypeKanji = 8
|
||||||
|
|
||||||
|
// Common character encodings
|
||||||
|
qrEciIos8859_1 = 1
|
||||||
|
qrEciIbm437 = 2
|
||||||
|
qrEciIos8859_2 = 4
|
||||||
|
qrEciIso8859_3 = 5
|
||||||
|
qrEciIso8859_4 = 6
|
||||||
|
qrEciIso8859_5 = 7
|
||||||
|
qrEciIso8859_6 = 8
|
||||||
|
qrEciIso8859_7 = 9
|
||||||
|
qrEciIso8859_8 = 10
|
||||||
|
qrEciIso8859_9 = 11
|
||||||
|
qrEciWindows874 = 13
|
||||||
|
qrEciIso8859_13 = 15
|
||||||
|
qrEciIso8859_15 = 17
|
||||||
|
qrEciShiftJis = 20
|
||||||
|
qrEciUtf8 = 26
|
||||||
|
)
|
||||||
146
vendor/github.com/liyue201/goqr/qrcode.go
generated
vendored
Normal file
146
vendor/github.com/liyue201/goqr/qrcode.go
generated
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
74
vendor/github.com/liyue201/goqr/recognition.go
generated
vendored
Normal file
74
vendor/github.com/liyue201/goqr/recognition.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package goqr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recognize recognizes the passed image and returns a slice of QRData.
|
||||||
|
func Recognize(img image.Image) ([]*QRData, error) {
|
||||||
|
b := img.Bounds()
|
||||||
|
|
||||||
|
r := NewRecognizer(b.Max.X, b.Max.Y)
|
||||||
|
r.Begin()
|
||||||
|
switch m := img.(type) {
|
||||||
|
case *image.Gray:
|
||||||
|
off := 0
|
||||||
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
||||||
|
for x := b.Min.X; x < b.Max.X; x++ {
|
||||||
|
gray := m.GrayAt(x, y)
|
||||||
|
r.SetPixel(x-b.Min.X, y-b.Min.Y, byte(gray.Y))
|
||||||
|
off++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *image.RGBA:
|
||||||
|
off := 0
|
||||||
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
||||||
|
for x := b.Min.X; x < b.Max.X; x++ {
|
||||||
|
pix := toGrayLuminance(m.At(x, y))
|
||||||
|
r.SetPixel(x-b.Min.X, y-b.Min.Y, byte(pix))
|
||||||
|
off++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
off := 0
|
||||||
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
||||||
|
for x := b.Min.X; x < b.Max.X; x++ {
|
||||||
|
rgba := color.RGBAModel.Convert(m.At(x, y)).(color.RGBA)
|
||||||
|
pix := toGrayLuminance(rgba)
|
||||||
|
r.SetPixel(x-b.Min.X, y-b.Min.Y, byte(pix))
|
||||||
|
off++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.End()
|
||||||
|
|
||||||
|
count := r.Count()
|
||||||
|
if count == 0 {
|
||||||
|
return nil, ErrNoQRCode
|
||||||
|
}
|
||||||
|
|
||||||
|
qrCodes := make([]*QRData, 0)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
code, err := r.Decode(i)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
qrCodes = append(qrCodes, code)
|
||||||
|
}
|
||||||
|
if len(qrCodes) == 0 {
|
||||||
|
return nil, ErrNoQRCode
|
||||||
|
}
|
||||||
|
return qrCodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toGrayLuminance(c color.Color) uint8 {
|
||||||
|
rr, gg, bb, _ := c.RGBA()
|
||||||
|
r := math.Pow(float64(rr), 2.2)
|
||||||
|
g := math.Pow(float64(gg), 2.2)
|
||||||
|
b := math.Pow(float64(bb), 2.2)
|
||||||
|
y := math.Pow(0.2125*r+0.7154*g+0.0721*b, 1/2.2)
|
||||||
|
Y := uint16(y + 0.5)
|
||||||
|
return uint8(Y >> 8)
|
||||||
|
}
|
||||||
976
vendor/github.com/liyue201/goqr/recognizer.go
generated
vendored
Normal file
976
vendor/github.com/liyue201/goqr/recognizer.go
generated
vendored
Normal file
@ -0,0 +1,976 @@
|
|||||||
|
package goqr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
thresholdSMin = 1
|
||||||
|
thresholdSDen = 8
|
||||||
|
thresholdT = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error definition
|
||||||
|
var (
|
||||||
|
ErrOutOfRange = errors.New("out of range")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recognizer is a Qr Code recognizer interface
|
||||||
|
type Recognizer interface {
|
||||||
|
SetPixel(x, y int, val uint8)
|
||||||
|
Begin()
|
||||||
|
End()
|
||||||
|
Count() int
|
||||||
|
Decode(index int) (*QRData, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type recognizer struct {
|
||||||
|
pixels []uint8
|
||||||
|
w int
|
||||||
|
h int
|
||||||
|
regions []qrRegion
|
||||||
|
capstones []qrCapstone
|
||||||
|
grids []qrGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRecognizer news a recognizer
|
||||||
|
func NewRecognizer(w, h int) Recognizer {
|
||||||
|
if w <= 0 || h <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &recognizer{
|
||||||
|
h: h,
|
||||||
|
w: w,
|
||||||
|
pixels: make([]qrPixelType, w*h),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) SetPixel(x, y int, val uint8) {
|
||||||
|
q.pixels[x+y*q.w] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) Count() int {
|
||||||
|
return len(q.grids)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) Begin() {
|
||||||
|
q.regions = make([]qrRegion, qrPixelRegion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) End() {
|
||||||
|
q.threshold()
|
||||||
|
for i := 0; i < q.h; i++ {
|
||||||
|
q.finderScan(i)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(q.capstones); i++ {
|
||||||
|
q.testGrouping(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) extract(index int) (*qrCode, error) {
|
||||||
|
code := &qrCode{}
|
||||||
|
|
||||||
|
qr := &q.grids[index]
|
||||||
|
|
||||||
|
if index < 0 || index >= len(q.grids) {
|
||||||
|
return nil, ErrOutOfRange
|
||||||
|
}
|
||||||
|
|
||||||
|
perspectiveMap(qr.c[:], 0.0, 0.0, &code.corners[0])
|
||||||
|
perspectiveMap(qr.c[:], float64(qr.gridSize), 0.0, &code.corners[1])
|
||||||
|
perspectiveMap(qr.c[:], float64(qr.gridSize), float64(qr.gridSize), &code.corners[2])
|
||||||
|
perspectiveMap(qr.c[:], 0.0, float64(qr.gridSize), &code.corners[3])
|
||||||
|
|
||||||
|
code.size = qr.gridSize
|
||||||
|
i := uint(0)
|
||||||
|
for y := 0; y < qr.gridSize; y++ {
|
||||||
|
for x := 0; x < qr.gridSize; x++ {
|
||||||
|
if q.readCell(index, x, y) > 0 {
|
||||||
|
code.cellBitmap[i>>3] |= uint8(1 << (i & 7))
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) Decode(index int) (*QRData, error) {
|
||||||
|
code, err := q.extract(index)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var data QRData
|
||||||
|
data.Payload = make([]uint8, 0)
|
||||||
|
|
||||||
|
err = decode(code, &data)
|
||||||
|
return &data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) threshold() {
|
||||||
|
var x, y int
|
||||||
|
avgW := 0
|
||||||
|
avgU := 0
|
||||||
|
thresholds := q.w / thresholdSDen
|
||||||
|
|
||||||
|
// Ensure a sane, non-zero value for threshold_s.
|
||||||
|
// threshold_s can be zero if the image width is small. We need to avoid
|
||||||
|
// SIGFPE as it will be used as divisor.
|
||||||
|
if thresholds < thresholdSMin {
|
||||||
|
thresholds = thresholdSMin
|
||||||
|
}
|
||||||
|
|
||||||
|
for y = 0; y < q.h; y++ {
|
||||||
|
row := q.pixels[q.w*y : q.w*(y+1)]
|
||||||
|
rowAverage := make([]int, q.w)
|
||||||
|
for x = 0; x < q.w; x++ {
|
||||||
|
var w, u int
|
||||||
|
if y&1 == 1 {
|
||||||
|
w = x
|
||||||
|
u = q.w - 1 - x
|
||||||
|
} else {
|
||||||
|
w = q.w - 1 - x
|
||||||
|
u = x
|
||||||
|
}
|
||||||
|
|
||||||
|
avgW = (avgW*(thresholds-1))/thresholds + int(row[w])
|
||||||
|
avgU = (avgU*(thresholds-1))/thresholds + int(row[u])
|
||||||
|
|
||||||
|
rowAverage[w] += avgW
|
||||||
|
rowAverage[u] += avgU
|
||||||
|
}
|
||||||
|
|
||||||
|
for x = 0; x < q.w; x++ {
|
||||||
|
if int(row[x]) < rowAverage[x]*(100-thresholdT)/(200*thresholds) {
|
||||||
|
row[x] = qrPixelBlack
|
||||||
|
} else {
|
||||||
|
row[x] = qrPixelWhite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const floodFileMaxDepth = 4096
|
||||||
|
|
||||||
|
type spanFunc func(userData interface{}, y, left, right int)
|
||||||
|
|
||||||
|
func (q *recognizer) floodFillSeed(x, y, from, to int, span spanFunc, userData interface{}, depth int) {
|
||||||
|
left := x
|
||||||
|
right := x
|
||||||
|
row := q.pixels[y*q.w : (y+1)*q.w]
|
||||||
|
|
||||||
|
if depth >= floodFileMaxDepth {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for left > 0 && int(row[left-1]) == from {
|
||||||
|
left--
|
||||||
|
}
|
||||||
|
|
||||||
|
for right < q.w-1 && int(row[right+1]) == from {
|
||||||
|
right++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the extent
|
||||||
|
for i := left; i <= right; i++ {
|
||||||
|
row[i] = qrPixelType(to)
|
||||||
|
}
|
||||||
|
|
||||||
|
if span != nil {
|
||||||
|
span(userData, y, left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seed new flood-fills
|
||||||
|
if y > 0 {
|
||||||
|
row = q.pixels[(y-1)*q.w : (y)*q.w]
|
||||||
|
for i := left; i <= right; i++ {
|
||||||
|
if int(row[i]) == from {
|
||||||
|
q.floodFillSeed(i, y-1, from, to, span, userData, depth+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if y < q.h-1 {
|
||||||
|
row = q.pixels[(y+1)*q.w : (y+2)*q.w]
|
||||||
|
for i := left; i <= right; i++ {
|
||||||
|
if int(row[i]) == from {
|
||||||
|
q.floodFillSeed(i, y+1, from, to, span, userData, depth+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func areaCount(userData interface{}, y, left, right int) {
|
||||||
|
region := userData.(*qrRegion)
|
||||||
|
region.count += right - left + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) regionCode(x, y int) int {
|
||||||
|
if x < 0 || y < 0 || x >= q.w || y >= q.h {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
pixel := int(q.pixels[y*q.w+x])
|
||||||
|
if pixel >= qrPixelRegion {
|
||||||
|
return pixel
|
||||||
|
}
|
||||||
|
if pixel == qrPixelWhite {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if len(q.regions) >= qrMaxRegion {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
region := len(q.regions)
|
||||||
|
q.regions = append(q.regions, qrRegion{})
|
||||||
|
box := &q.regions[region]
|
||||||
|
|
||||||
|
box.seed.x = x
|
||||||
|
box.seed.y = y
|
||||||
|
box.count = 0
|
||||||
|
box.capstone = -1
|
||||||
|
|
||||||
|
q.floodFillSeed(x, y, pixel, region, areaCount, box, 0)
|
||||||
|
return region
|
||||||
|
}
|
||||||
|
|
||||||
|
func findOneCorner(userData interface{}, y, left, right int) {
|
||||||
|
psd := userData.(*polygonScoreData)
|
||||||
|
xs := [2]int{left, right}
|
||||||
|
dy := y - psd.ref.y
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
dx := xs[i] - psd.ref.x
|
||||||
|
d := dx*dx + dy*dy
|
||||||
|
if d > psd.scores[0] {
|
||||||
|
psd.scores[0] = d
|
||||||
|
psd.corners[0].x = xs[i]
|
||||||
|
psd.corners[0].y = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findOtherCorners(userData interface{}, y, left, right int) {
|
||||||
|
psd := userData.(*polygonScoreData)
|
||||||
|
xs := [2]int{left, right}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
up := xs[i]*psd.ref.x + y*psd.ref.y
|
||||||
|
right := xs[i]*-psd.ref.y + y*psd.ref.x
|
||||||
|
scores := [4]int{up, right, -up, -right}
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
if scores[j] > psd.scores[j] {
|
||||||
|
psd.scores[j] = scores[j]
|
||||||
|
psd.corners[j].x = xs[i]
|
||||||
|
psd.corners[j].y = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) findRegionCorners(rcode int, ref *point, corners []point) {
|
||||||
|
region := &q.regions[rcode]
|
||||||
|
psd := polygonScoreData{}
|
||||||
|
psd.corners = corners[:]
|
||||||
|
|
||||||
|
psd.ref = *ref
|
||||||
|
psd.scores[0] = -1
|
||||||
|
|
||||||
|
q.floodFillSeed(region.seed.x, region.seed.y, rcode, qrPixelBlack, findOneCorner, &psd, 0)
|
||||||
|
|
||||||
|
psd.ref.x = psd.corners[0].x - psd.ref.x
|
||||||
|
psd.ref.y = psd.corners[0].y - psd.ref.y
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
psd.corners[i] = region.seed
|
||||||
|
}
|
||||||
|
|
||||||
|
i := region.seed.x*psd.ref.x + region.seed.y*psd.ref.y
|
||||||
|
psd.scores[0] = i
|
||||||
|
psd.scores[2] = -i
|
||||||
|
|
||||||
|
i = region.seed.x*-psd.ref.y + region.seed.y*psd.ref.x
|
||||||
|
psd.scores[1] = i
|
||||||
|
psd.scores[3] = -i
|
||||||
|
|
||||||
|
q.floodFillSeed(region.seed.x, region.seed.y, qrPixelBlack, rcode, findOtherCorners, &psd, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) recordCapstone(ring, stone int) {
|
||||||
|
|
||||||
|
stoneReg := &q.regions[stone]
|
||||||
|
ringReg := &q.regions[ring]
|
||||||
|
|
||||||
|
if len(q.capstones) >= qrMaxCastones {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
csIndex := len(q.capstones)
|
||||||
|
q.capstones = append(q.capstones, qrCapstone{})
|
||||||
|
capstone := &q.capstones[csIndex]
|
||||||
|
|
||||||
|
capstone.qrGrid = -1
|
||||||
|
capstone.ring = ring
|
||||||
|
capstone.stone = stone
|
||||||
|
stoneReg.capstone = csIndex
|
||||||
|
ringReg.capstone = csIndex
|
||||||
|
|
||||||
|
// Find the corners of the ring
|
||||||
|
q.findRegionCorners(ring, &stoneReg.seed, capstone.corners[:])
|
||||||
|
|
||||||
|
// Set up the perspective transform and find the center
|
||||||
|
perspectiveSetup(capstone.c[:], capstone.corners[:], 7.0, 7.0)
|
||||||
|
perspectiveMap(capstone.c[:], 3.5, 3.5, &capstone.center)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func perspectiveSetup(c []float64, rect []point, w, h float64) {
|
||||||
|
x0 := float64(rect[0].x)
|
||||||
|
y0 := float64(rect[0].y)
|
||||||
|
x1 := float64(rect[1].x)
|
||||||
|
y1 := float64(rect[1].y)
|
||||||
|
x2 := float64(rect[2].x)
|
||||||
|
y2 := float64(rect[2].y)
|
||||||
|
x3 := float64(rect[3].x)
|
||||||
|
y3 := float64(rect[3].y)
|
||||||
|
wden := w * (x2*y3 - x3*y2 + (x3-x2)*y1 + x1*(y2-y3))
|
||||||
|
hden := h * (x2*y3 + x1*(y2-y3) - x3*y2 + (x3-x2)*y1)
|
||||||
|
c[0] = (x1*(x2*y3-x3*y2) + x0*(-x2*y3+x3*y2+(x2-x3)*y1) + x1*(x3-x2)*y0) / wden
|
||||||
|
c[1] = -(x0*(x2*y3+x1*(y2-y3)-x2*y1) - x1*x3*y2 + x2*x3*y1 + (x1*x3-x2*x3)*y0) / hden
|
||||||
|
c[2] = x0
|
||||||
|
c[3] = (y0*(x1*(y3-y2)-x2*y3+x3*y2) + y1*(x2*y3-x3*y2) + x0*y1*(y2-y3)) / wden
|
||||||
|
c[4] = (x0*(y1*y3-y2*y3) + x1*y2*y3 - x2*y1*y3 + y0*(x3*y2-x1*y2+(x2-x3)*y1)) / hden
|
||||||
|
c[5] = y0
|
||||||
|
c[6] = (x1*(y3-y2) + x0*(y2-y3) + (x2-x3)*y1 + (x3-x2)*y0) / wden
|
||||||
|
c[7] = (-x2*y3 + x1*y3 + x3*y2 + x0*(y1-y2) - x3*y1 + (x2-x1)*y0) / hden
|
||||||
|
}
|
||||||
|
|
||||||
|
func perspectiveMap(c []float64, u, v float64, ret *point) {
|
||||||
|
den := c[6]*u + c[7]*v + 1.0
|
||||||
|
x := (c[0]*u + c[1]*v + c[2]) / den
|
||||||
|
y := (c[3]*u + c[4]*v + c[5]) / den
|
||||||
|
ret.x = int(x + 0.5)
|
||||||
|
ret.y = int(y + 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func perspectiveUnmap(c []float64, in *point, u, v *float64) {
|
||||||
|
x := float64(in.x)
|
||||||
|
y := float64(in.y)
|
||||||
|
den := -c[0]*c[7]*y + c[1]*c[6]*y + (c[3]*c[7]-c[4]*c[6])*x + c[0]*c[4] - c[1]*c[3]
|
||||||
|
*u = -(c[1]*(y-c[5]) - c[2]*c[7]*y + (c[5]*c[7]-c[4])*x + c[2]*c[4]) / den
|
||||||
|
*v = (c[0]*(y-c[5]) - c[2]*c[6]*y + (c[5]*c[6]-c[3])*x + c[2]*c[3]) / den
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) testCapstone(x, y int, pb []int) {
|
||||||
|
|
||||||
|
ringRight := q.regionCode(x-pb[4], y)
|
||||||
|
stone := q.regionCode(x-pb[4]-pb[3]-pb[2], y)
|
||||||
|
ringLeft := q.regionCode(x-pb[4]-pb[3]-pb[2]-pb[1]-pb[0], y)
|
||||||
|
|
||||||
|
if ringLeft < 0 || ringRight < 0 || stone < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Left and ring of ring should be connected
|
||||||
|
if ringLeft != ringRight {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Ring should be disconnected from stone
|
||||||
|
if ringLeft == stone {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stoneReg := &q.regions[stone]
|
||||||
|
ringReg := &q.regions[ringLeft]
|
||||||
|
/* Already detected */
|
||||||
|
if stoneReg.capstone >= 0 || ringReg.capstone >= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Ratio should ideally be 37.5
|
||||||
|
ratio := stoneReg.count * 100 / ringReg.count
|
||||||
|
if ratio < 10 || ratio > 70 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
q.recordCapstone(ringLeft, stone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) finderScan(y int) {
|
||||||
|
|
||||||
|
row := q.pixels[y*q.w : (y+1)*q.w]
|
||||||
|
x := 0
|
||||||
|
lastColor := 0
|
||||||
|
runLength := 0
|
||||||
|
runCount := 0
|
||||||
|
pb := make([]int, 5)
|
||||||
|
check := [5]int{1, 1, 3, 1, 1}
|
||||||
|
|
||||||
|
for x = 0; x < q.w; x++ {
|
||||||
|
color := 0
|
||||||
|
if row[x] > 0 {
|
||||||
|
color = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if x > 0 && color != lastColor {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
pb[i] = pb[i+1]
|
||||||
|
}
|
||||||
|
pb[4] = runLength
|
||||||
|
runLength = 0
|
||||||
|
runCount++
|
||||||
|
|
||||||
|
if color == 0 && runCount >= 5 {
|
||||||
|
var avg, err int
|
||||||
|
ok := true
|
||||||
|
avg = (pb[0] + pb[1] + pb[3] + pb[4]) / 4
|
||||||
|
err = avg * 3 / 4
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if pb[i] < check[i]*avg-err || pb[i] > check[i]*avg+err {
|
||||||
|
ok = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
q.testCapstone(x, y, pb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runLength++
|
||||||
|
lastColor = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) findAlignmentPattern(index int) {
|
||||||
|
qr := &q.grids[index]
|
||||||
|
c0 := &q.capstones[qr.caps[0]]
|
||||||
|
c2 := &q.capstones[qr.caps[2]]
|
||||||
|
|
||||||
|
var a, b, c point
|
||||||
|
stepSize := 1
|
||||||
|
dir := 0
|
||||||
|
var u, v float64
|
||||||
|
|
||||||
|
// Grab our previous estimate of the alignment pattern corner
|
||||||
|
b = qr.align
|
||||||
|
|
||||||
|
// Guess another two corners of the alignment pattern so that we
|
||||||
|
// can estimate its size.
|
||||||
|
|
||||||
|
perspectiveUnmap(c0.c[:], &b, &u, &v)
|
||||||
|
perspectiveMap(c0.c[:], u, v+1.0, &a)
|
||||||
|
perspectiveUnmap(c2.c[:], &b, &u, &v)
|
||||||
|
perspectiveMap(c2.c[:], u+1.0, v, &c)
|
||||||
|
sizeEstimate := int(math.Abs(float64((a.x-b.x)*-(c.y-b.y) + (a.y-b.y)*(c.x-b.x))))
|
||||||
|
|
||||||
|
// Spiral outwards from the estimate point until we find something
|
||||||
|
// roughly the right size. Don't look too far from the estimate point
|
||||||
|
|
||||||
|
for stepSize*stepSize < sizeEstimate*100 {
|
||||||
|
dxMap := []int{1, 0, -1, 0}
|
||||||
|
dyMap := []int{0, -1, 0, 1}
|
||||||
|
|
||||||
|
for i := 0; i < stepSize; i++ {
|
||||||
|
code := q.regionCode(b.x, b.y)
|
||||||
|
|
||||||
|
if code >= 0 {
|
||||||
|
reg := &q.regions[code]
|
||||||
|
|
||||||
|
if reg.count >= sizeEstimate/2 && reg.count <= sizeEstimate*2 {
|
||||||
|
qr.alignRegion = code
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.x += dxMap[dir]
|
||||||
|
b.y += dyMap[dir]
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = (dir + 1) % 4
|
||||||
|
if (dir & 1) == 1 {
|
||||||
|
stepSize++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readCell read a cell from a grid using the currently set perspective
|
||||||
|
// transform. Returns +/- 1 for black/white, 0 for cells which are
|
||||||
|
// out of image bounds.
|
||||||
|
func (q *recognizer) readCell(index, x, y int) int {
|
||||||
|
qr := &q.grids[index]
|
||||||
|
var p point
|
||||||
|
|
||||||
|
perspectiveMap(qr.c[:], float64(x)+0.5, float64(y)+0.5, &p)
|
||||||
|
if p.y < 0 || p.y >= q.h || p.x < 0 || p.x >= q.w {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if q.pixels[p.y*q.w+p.x] != 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) fitnessCell(index, x, y int) int {
|
||||||
|
qr := &q.grids[index]
|
||||||
|
score := 0
|
||||||
|
offsets := []float64{0.3, 0.5, 0.7}
|
||||||
|
for v := 0; v < 3; v++ {
|
||||||
|
for u := 0; u < 3; u++ {
|
||||||
|
var p point
|
||||||
|
perspectiveMap(qr.c[:], float64(x)+offsets[u], float64(y)+offsets[v], &p)
|
||||||
|
|
||||||
|
if p.y < 0 || p.y >= q.h || p.x < 0 || p.x >= q.w {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if q.pixels[p.y*q.w+p.x] != 0 {
|
||||||
|
score++
|
||||||
|
} else {
|
||||||
|
score--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) fitnessRing(index, cx, cy, radius int) int {
|
||||||
|
score := 0
|
||||||
|
for i := 0; i < radius*2; i++ {
|
||||||
|
score += q.fitnessCell(index, cx-radius+i, cy-radius)
|
||||||
|
score += q.fitnessCell(index, cx-radius, cy+radius-i)
|
||||||
|
score += q.fitnessCell(index, cx+radius, cy-radius+i)
|
||||||
|
score += q.fitnessCell(index, cx+radius-i, cy+radius)
|
||||||
|
}
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) fitnessApat(index, cx, cy int) int {
|
||||||
|
return q.fitnessCell(index, cx, cy) -
|
||||||
|
q.fitnessRing(index, cx, cy, 1) +
|
||||||
|
q.fitnessRing(index, cx, cy, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) fitnessCapstone(index, x, y int) int {
|
||||||
|
x += 3
|
||||||
|
y += 3
|
||||||
|
return q.fitnessCell(index, x, y) +
|
||||||
|
q.fitnessRing(index, x, y, 1) -
|
||||||
|
q.fitnessRing(index, x, y, 2) +
|
||||||
|
q.fitnessRing(index, x, y, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fitnessAll compute a fitness score for the currently configured perspective
|
||||||
|
// transform, using the features we expect to find by scanning the
|
||||||
|
// grid.
|
||||||
|
func (q *recognizer) fitnessAll(index int) int {
|
||||||
|
qr := &q.grids[index]
|
||||||
|
version := (qr.gridSize - 17) / 4
|
||||||
|
info := &qrVersionDb[version]
|
||||||
|
score := 0
|
||||||
|
|
||||||
|
// Check the timing pattern
|
||||||
|
for i := 0; i < qr.gridSize-14; i++ {
|
||||||
|
expect := 1
|
||||||
|
if i&1 == 0 {
|
||||||
|
expect = -1
|
||||||
|
}
|
||||||
|
score += q.fitnessCell(index, i+7, 6) * expect
|
||||||
|
score += q.fitnessCell(index, 6, i+7) * expect
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check capstones
|
||||||
|
score += q.fitnessCapstone(index, 0, 0)
|
||||||
|
score += q.fitnessCapstone(index, qr.gridSize-7, 0)
|
||||||
|
score += q.fitnessCapstone(index, 0, qr.gridSize-7)
|
||||||
|
|
||||||
|
if version < 0 || version > qrMaxVersion {
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check alignment patterns
|
||||||
|
apCount := 0
|
||||||
|
for (apCount < qrMaxAliment) && info.apat[apCount] != 0 {
|
||||||
|
apCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i+1 < apCount; i++ {
|
||||||
|
score += q.fitnessApat(index, 6, info.apat[i])
|
||||||
|
score += q.fitnessApat(index, info.apat[i], 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < apCount; i++ {
|
||||||
|
for j := 1; j < apCount; j++ {
|
||||||
|
score += q.fitnessApat(index, info.apat[i], info.apat[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) jigglePerspective(index int) {
|
||||||
|
qr := &q.grids[index]
|
||||||
|
best := q.fitnessAll(index)
|
||||||
|
|
||||||
|
adjustments := make([]float64, 8)
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
adjustments[i] = qr.c[i] * 0.02
|
||||||
|
}
|
||||||
|
|
||||||
|
for pass := 0; pass < 5; pass++ {
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
j := i >> 1
|
||||||
|
old := qr.c[j]
|
||||||
|
step := adjustments[j]
|
||||||
|
var new float64
|
||||||
|
|
||||||
|
if i&1 == 1 {
|
||||||
|
new = old + step
|
||||||
|
} else {
|
||||||
|
new = old - step
|
||||||
|
}
|
||||||
|
qr.c[j] = new
|
||||||
|
test := q.fitnessAll(index)
|
||||||
|
|
||||||
|
if test > best {
|
||||||
|
best = test
|
||||||
|
} else {
|
||||||
|
qr.c[j] = old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
adjustments[i] *= 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the capstones are in place and an alignment point has been chosen,
|
||||||
|
// we call this function to set up a grid-reading perspective transform.
|
||||||
|
func (q *recognizer) setupQrPerspective(index int) {
|
||||||
|
qr := &q.grids[index]
|
||||||
|
var rect [4]point
|
||||||
|
|
||||||
|
/* Set up the perspective map for reading the grid */
|
||||||
|
rect[0] = q.capstones[qr.caps[1]].corners[0]
|
||||||
|
rect[1] = q.capstones[qr.caps[2]].corners[0]
|
||||||
|
rect[2] = qr.align
|
||||||
|
rect[3] = q.capstones[qr.caps[0]].corners[0]
|
||||||
|
|
||||||
|
perspectiveSetup(qr.c[:], rect[:], float64(qr.gridSize-7), float64(qr.gridSize-7))
|
||||||
|
q.jigglePerspective(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotateCapstone(cap *qrCapstone, h0, hd *point) {
|
||||||
|
copy := [4]point{}
|
||||||
|
|
||||||
|
var best int
|
||||||
|
var bestScore int
|
||||||
|
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
p := &cap.corners[j]
|
||||||
|
score := (p.x-h0.x)*-hd.y + (p.y-h0.y)*hd.x
|
||||||
|
if j == 0 || score < bestScore {
|
||||||
|
best = j
|
||||||
|
bestScore = score
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///* Rotate the capstone */
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
copy[j] = cap.corners[(j+best)%4]
|
||||||
|
}
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
cap.corners[j] = copy[j]
|
||||||
|
}
|
||||||
|
perspectiveSetup(cap.c[:], cap.corners[:], 7.0, 7.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) timingScan(p0, p1 *point) int {
|
||||||
|
n := p1.x - p0.x
|
||||||
|
d := p1.y - p0.y
|
||||||
|
x := p0.x
|
||||||
|
y := p0.y
|
||||||
|
a := 0
|
||||||
|
runlength := 0
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
var dom, nondom *int
|
||||||
|
var domStep int
|
||||||
|
var nondomStep int
|
||||||
|
|
||||||
|
if p0.x < 0 || p0.y < 0 || p0.x >= q.w || p0.y >= q.h {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if p1.x < 0 || p1.y < 0 || p1.x >= q.w || p1.y >= q.h {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if math.Abs(float64(n)) > math.Abs(float64(d)) {
|
||||||
|
n, d = d, n
|
||||||
|
dom = &x
|
||||||
|
nondom = &y
|
||||||
|
} else {
|
||||||
|
dom = &y
|
||||||
|
nondom = &x
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < 0 {
|
||||||
|
n = -n
|
||||||
|
nondomStep = -1
|
||||||
|
} else {
|
||||||
|
nondomStep = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if d < 0 {
|
||||||
|
d = -d
|
||||||
|
domStep = -1
|
||||||
|
} else {
|
||||||
|
domStep = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
x = p0.x
|
||||||
|
y = p0.y
|
||||||
|
for i := 0; i <= d; i++ {
|
||||||
|
if y < 0 || y >= q.h || x < 0 || x >= q.w {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pixel := q.pixels[y*q.w+x]
|
||||||
|
|
||||||
|
if pixel > 0 {
|
||||||
|
if runlength >= 2 {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
runlength = 0
|
||||||
|
} else {
|
||||||
|
runlength++
|
||||||
|
}
|
||||||
|
|
||||||
|
a += n
|
||||||
|
*dom += domStep
|
||||||
|
if a >= d {
|
||||||
|
*nondom += nondomStep
|
||||||
|
a -= d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLeftMostToLine(userData interface{}, y, left, right int) {
|
||||||
|
psd := userData.(*polygonScoreData)
|
||||||
|
xs := []int{left, right}
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
d := -psd.ref.y*xs[i] + psd.ref.x*y
|
||||||
|
if d < psd.scores[0] {
|
||||||
|
psd.scores[0] = d
|
||||||
|
psd.corners[0].x = xs[i]
|
||||||
|
psd.corners[0].y = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the measure the timing pattern for a given QR code. This does
|
||||||
|
// not require the global perspective to have been set up, but it
|
||||||
|
// does require that the capstone corners have been set to their
|
||||||
|
// canonical rotation.
|
||||||
|
//
|
||||||
|
// For each capstone, we find a point in the middle of the ring band
|
||||||
|
// which is nearest the centre of the code. Using these points, we do
|
||||||
|
// a horizontal and a vertical timing scan.
|
||||||
|
func (q *recognizer) measureTimingPattern(index int) int {
|
||||||
|
qr := &q.grids[index]
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
us := []float64{6.5, 6.5, 0.5}
|
||||||
|
vs := []float64{0.5, 6.5, 6.5}
|
||||||
|
cap := &q.capstones[qr.caps[i]]
|
||||||
|
perspectiveMap(cap.c[:], us[i], vs[i], &qr.tpep[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
qr.hscan = q.timingScan(&qr.tpep[1], &qr.tpep[2])
|
||||||
|
qr.vscan = q.timingScan(&qr.tpep[1], &qr.tpep[0])
|
||||||
|
scan := qr.hscan
|
||||||
|
if qr.vscan > scan {
|
||||||
|
scan = qr.vscan
|
||||||
|
}
|
||||||
|
|
||||||
|
// If neither scan worked, we can't go any further.
|
||||||
|
if scan < 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose the nearest allowable grid size
|
||||||
|
size := scan*2 + 13
|
||||||
|
ver := (size - 15) / 4
|
||||||
|
qr.gridSize = ver*4 + 17
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) recordQrGrid(a, b, c int) {
|
||||||
|
|
||||||
|
if len(q.grids) >= qrMaxGrids {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the hypotenuse line from A to C. B should be tothe left of this line.
|
||||||
|
|
||||||
|
h0 := q.capstones[a].center
|
||||||
|
var hd point
|
||||||
|
hd.x = q.capstones[c].center.x - q.capstones[a].center.x
|
||||||
|
hd.y = q.capstones[c].center.y - q.capstones[a].center.y
|
||||||
|
|
||||||
|
// Make sure A-B-C is clockwise
|
||||||
|
if (q.capstones[b].center.x-h0.x)*-hd.y+(q.capstones[b].center.y-h0.y)*hd.x > 0 {
|
||||||
|
a, c = c, a
|
||||||
|
hd.x = -hd.x
|
||||||
|
hd.y = -hd.y
|
||||||
|
}
|
||||||
|
|
||||||
|
qrIndex := len(q.grids)
|
||||||
|
q.grids = append(q.grids, qrGrid{})
|
||||||
|
qr := &q.grids[qrIndex]
|
||||||
|
|
||||||
|
qr.caps[0] = a
|
||||||
|
qr.caps[1] = b
|
||||||
|
qr.caps[2] = c
|
||||||
|
qr.alignRegion = -1
|
||||||
|
|
||||||
|
// Rotate each capstone so that corner 0 is top-left with respect
|
||||||
|
// to the grid.
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
cap := &q.capstones[qr.caps[i]]
|
||||||
|
rotateCapstone(cap, &h0, &hd)
|
||||||
|
cap.qrGrid = qrIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the timing pattern. This doesn't require a perspective transform.
|
||||||
|
|
||||||
|
if q.measureTimingPattern(qrIndex) < 0 {
|
||||||
|
goto fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make an estimate based for the alignment pattern based on extending lines from capstones A and C.
|
||||||
|
if !lineIntersect(&q.capstones[a].corners[0],
|
||||||
|
&q.capstones[a].corners[1],
|
||||||
|
&q.capstones[c].corners[0],
|
||||||
|
&q.capstones[c].corners[3],
|
||||||
|
&qr.align) {
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// On V2+ grids, we should use the alignment pattern.
|
||||||
|
|
||||||
|
if qr.gridSize > 21 {
|
||||||
|
// Try to find the actual location of the alignment pattern.
|
||||||
|
|
||||||
|
q.findAlignmentPattern(qrIndex)
|
||||||
|
|
||||||
|
// Find the point of the alignment pattern closest to the
|
||||||
|
// top-left of the QR grid.
|
||||||
|
|
||||||
|
if qr.alignRegion >= 0 {
|
||||||
|
var psd polygonScoreData
|
||||||
|
psd.corners = make([]point, 1)
|
||||||
|
reg := &q.regions[qr.alignRegion]
|
||||||
|
|
||||||
|
// Start from some point inside the alignment pattern
|
||||||
|
qr.align = reg.seed
|
||||||
|
psd.ref = hd
|
||||||
|
psd.corners[0] = qr.align
|
||||||
|
|
||||||
|
psd.scores[0] = -hd.y*qr.align.x + hd.x*qr.align.y
|
||||||
|
|
||||||
|
q.floodFillSeed(reg.seed.x, reg.seed.y, qr.alignRegion, qrPixelBlack, nil, nil, 0)
|
||||||
|
|
||||||
|
q.floodFillSeed(reg.seed.x, reg.seed.y, qrPixelBlack, qr.alignRegion, findLeftMostToLine, &psd, 0)
|
||||||
|
qr.align = psd.corners[0]
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q.setupQrPerspective(qrIndex)
|
||||||
|
return
|
||||||
|
|
||||||
|
// We've been unable to complete setup for this grid. Undo what we've
|
||||||
|
// recorded and pretend it never happened.
|
||||||
|
|
||||||
|
fail:
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
q.capstones[qr.caps[i]].qrGrid = -1
|
||||||
|
}
|
||||||
|
q.grids = q.grids[:len(q.grids)-1]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) testNeighbours(i int, hlist []*neighbour, vlist []*neighbour) {
|
||||||
|
bestScore := 0.0
|
||||||
|
bestH := -1
|
||||||
|
bestV := -1
|
||||||
|
|
||||||
|
// Test each possible grouping
|
||||||
|
|
||||||
|
for j := 0; j < len(hlist); j++ {
|
||||||
|
hn := hlist[j]
|
||||||
|
|
||||||
|
for k := 0; k < len(vlist); k++ {
|
||||||
|
vn := vlist[k]
|
||||||
|
score := math.Abs(1.0 - hn.distance/vn.distance)
|
||||||
|
|
||||||
|
if score > 2.5 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if bestH < 0 || score < bestScore {
|
||||||
|
bestH = hn.index
|
||||||
|
bestV = vn.index
|
||||||
|
bestScore = score
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bestH < 0 || bestV < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
q.recordQrGrid(bestH, i, bestV)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *recognizer) testGrouping(i int) {
|
||||||
|
c1 := &q.capstones[i]
|
||||||
|
|
||||||
|
hlist := make([]*neighbour, 0)
|
||||||
|
vlist := make([]*neighbour, 0)
|
||||||
|
|
||||||
|
if c1.qrGrid >= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for potential neighbours by examining the relative gradients
|
||||||
|
// from this capstone to others.
|
||||||
|
|
||||||
|
for j := 0; j < len(q.capstones); j++ {
|
||||||
|
c2 := &q.capstones[j]
|
||||||
|
|
||||||
|
if i == j || c2.qrGrid >= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var u, v float64
|
||||||
|
|
||||||
|
perspectiveUnmap(c1.c[:], &c2.center, &u, &v)
|
||||||
|
|
||||||
|
u = math.Abs(u - 3.5)
|
||||||
|
v = math.Abs(v - 3.5)
|
||||||
|
|
||||||
|
if u < 0.2*v {
|
||||||
|
n := &neighbour{}
|
||||||
|
n.index = j
|
||||||
|
n.distance = v
|
||||||
|
hlist = append(hlist, n)
|
||||||
|
}
|
||||||
|
if v < 0.2*u {
|
||||||
|
n := &neighbour{}
|
||||||
|
n.index = j
|
||||||
|
n.distance = u
|
||||||
|
vlist = append(vlist, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(hlist) > 0 && len(vlist) > 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
q.testNeighbours(i, hlist, vlist)
|
||||||
|
}
|
||||||
31
vendor/github.com/liyue201/goqr/utils.go
generated
vendored
Normal file
31
vendor/github.com/liyue201/goqr/utils.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package goqr
|
||||||
|
|
||||||
|
func lineIntersect(p0, p1, q0, q1, r *point) bool {
|
||||||
|
// (a, b) is perpendicular to line p
|
||||||
|
a := -(p1.y - p0.y)
|
||||||
|
b := p1.x - p0.x
|
||||||
|
|
||||||
|
// (c, d) is perpendicular to line q
|
||||||
|
c := -(q1.y - q0.y)
|
||||||
|
d := q1.x - q0.x
|
||||||
|
|
||||||
|
// e and f are dot products of the respective vectors with p and q
|
||||||
|
e := a*p1.x + b*p1.y
|
||||||
|
f := c*q1.x + d*q1.y
|
||||||
|
|
||||||
|
// Now we need to solve:
|
||||||
|
// [a b] [rx] [e]
|
||||||
|
// [c d] [ry] = [f]
|
||||||
|
//
|
||||||
|
// We do this by inverting the matrix and applying it to (e, f):
|
||||||
|
// [ d -b] [e] [rx]
|
||||||
|
// 1/det [-c a] [f] = [ry]
|
||||||
|
//
|
||||||
|
det := (a * d) - (b * c)
|
||||||
|
if det == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
r.x = (d*e - b*f) / det
|
||||||
|
r.y = (-c*e + a*f) / det
|
||||||
|
return true
|
||||||
|
}
|
||||||
405
vendor/github.com/liyue201/goqr/version_db.go
generated
vendored
Normal file
405
vendor/github.com/liyue201/goqr/version_db.go
generated
vendored
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
package goqr
|
||||||
|
|
||||||
|
var qrVersionDb = [qrMaxVersion + 1]qrVersionInfo{
|
||||||
|
{},
|
||||||
|
{ // Version 1
|
||||||
|
26,
|
||||||
|
[qrMaxAliment]int{0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{26, 16, 1},
|
||||||
|
{26, 19, 1},
|
||||||
|
{26, 9, 1},
|
||||||
|
{26, 13, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 2
|
||||||
|
44,
|
||||||
|
[qrMaxAliment]int{6, 18, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{44, 28, 1},
|
||||||
|
{44, 34, 1},
|
||||||
|
{44, 16, 1},
|
||||||
|
{44, 22, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 3
|
||||||
|
70,
|
||||||
|
[qrMaxAliment]int{6, 22, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{70, 44, 1},
|
||||||
|
{70, 55, 1},
|
||||||
|
{35, 13, 2},
|
||||||
|
{35, 17, 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 4
|
||||||
|
100,
|
||||||
|
[qrMaxAliment]int{6, 26, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{50, 32, 2},
|
||||||
|
{100, 80, 1},
|
||||||
|
{25, 9, 4},
|
||||||
|
{50, 24, 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 5
|
||||||
|
134,
|
||||||
|
[qrMaxAliment]int{6, 30, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{67, 43, 2},
|
||||||
|
{134, 108, 1},
|
||||||
|
{33, 11, 2},
|
||||||
|
{33, 15, 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 6
|
||||||
|
172,
|
||||||
|
[qrMaxAliment]int{6, 34, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{43, 27, 4},
|
||||||
|
{86, 68, 2},
|
||||||
|
{43, 15, 4},
|
||||||
|
{43, 19, 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 7
|
||||||
|
196,
|
||||||
|
[qrMaxAliment]int{6, 22, 38, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{49, 31, 4},
|
||||||
|
{98, 78, 2},
|
||||||
|
{39, 13, 4},
|
||||||
|
{32, 14, 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 8
|
||||||
|
242,
|
||||||
|
[qrMaxAliment]int{6, 24, 42, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{60, 38, 2},
|
||||||
|
{121, 97, 2},
|
||||||
|
{40, 14, 4},
|
||||||
|
{40, 18, 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 9
|
||||||
|
292,
|
||||||
|
[qrMaxAliment]int{6, 26, 46, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{58, 36, 3},
|
||||||
|
{146, 116, 2},
|
||||||
|
{36, 12, 4},
|
||||||
|
{36, 16, 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 10
|
||||||
|
346,
|
||||||
|
[qrMaxAliment]int{6, 28, 50, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{69, 43, 4},
|
||||||
|
{86, 68, 2},
|
||||||
|
{43, 15, 6},
|
||||||
|
{43, 19, 6},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 11
|
||||||
|
404,
|
||||||
|
[qrMaxAliment]int{6, 30, 54, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{80, 50, 1},
|
||||||
|
{101, 81, 4},
|
||||||
|
{36, 12, 3},
|
||||||
|
{50, 22, 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 12
|
||||||
|
466,
|
||||||
|
[qrMaxAliment]int{6, 32, 58, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{58, 36, 6},
|
||||||
|
{116, 92, 2},
|
||||||
|
{42, 14, 7},
|
||||||
|
{46, 20, 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 13
|
||||||
|
532,
|
||||||
|
[qrMaxAliment]int{6, 34, 62, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{59, 37, 8},
|
||||||
|
{133, 107, 4},
|
||||||
|
{33, 11, 12},
|
||||||
|
{44, 20, 8},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 14
|
||||||
|
581,
|
||||||
|
[qrMaxAliment]int{6, 26, 46, 66, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{64, 40, 4},
|
||||||
|
{145, 115, 3},
|
||||||
|
{36, 12, 11},
|
||||||
|
{36, 16, 11},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 15
|
||||||
|
655,
|
||||||
|
[qrMaxAliment]int{6, 26, 48, 70, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{65, 41, 5},
|
||||||
|
{109, 87, 5},
|
||||||
|
{36, 12, 11},
|
||||||
|
{54, 24, 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 16
|
||||||
|
733,
|
||||||
|
[qrMaxAliment]int{6, 26, 50, 74, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{73, 45, 7},
|
||||||
|
{122, 98, 5},
|
||||||
|
{45, 15, 3},
|
||||||
|
{43, 19, 15},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 17
|
||||||
|
815,
|
||||||
|
[qrMaxAliment]int{6, 30, 54, 78, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{74, 46, 10},
|
||||||
|
{135, 107, 1},
|
||||||
|
{42, 14, 2},
|
||||||
|
{50, 22, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 18
|
||||||
|
901,
|
||||||
|
[qrMaxAliment]int{6, 30, 56, 82, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{69, 43, 9},
|
||||||
|
{150, 120, 5},
|
||||||
|
{42, 14, 2},
|
||||||
|
{50, 22, 17},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 19
|
||||||
|
991,
|
||||||
|
[qrMaxAliment]int{6, 30, 58, 86, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{70, 44, 3},
|
||||||
|
{141, 113, 3},
|
||||||
|
{39, 13, 9},
|
||||||
|
{47, 21, 17},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 20
|
||||||
|
1085,
|
||||||
|
[qrMaxAliment]int{6, 34, 62, 90, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{67, 41, 3},
|
||||||
|
{135, 107, 3},
|
||||||
|
{43, 15, 15},
|
||||||
|
{54, 24, 15},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 21
|
||||||
|
1156,
|
||||||
|
[qrMaxAliment]int{6, 28, 50, 72, 92, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{68, 42, 17},
|
||||||
|
{144, 116, 4},
|
||||||
|
{46, 16, 19},
|
||||||
|
{50, 22, 17},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 22
|
||||||
|
1258,
|
||||||
|
[qrMaxAliment]int{6, 26, 50, 74, 98, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{74, 46, 17},
|
||||||
|
{139, 111, 2},
|
||||||
|
{37, 13, 34},
|
||||||
|
{54, 24, 7},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 23
|
||||||
|
1364,
|
||||||
|
[qrMaxAliment]int{6, 30, 54, 78, 102, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{75, 47, 4},
|
||||||
|
{151, 121, 4},
|
||||||
|
{45, 15, 16},
|
||||||
|
{54, 24, 11},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 24
|
||||||
|
1474,
|
||||||
|
[qrMaxAliment]int{6, 28, 54, 80, 106, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{73, 45, 6},
|
||||||
|
{147, 117, 6},
|
||||||
|
{46, 16, 30},
|
||||||
|
{54, 24, 11},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 25
|
||||||
|
1588,
|
||||||
|
[qrMaxAliment]int{6, 32, 58, 84, 110, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{75, 47, 8},
|
||||||
|
{132, 106, 8},
|
||||||
|
{45, 15, 22},
|
||||||
|
{54, 24, 7},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 26
|
||||||
|
1706,
|
||||||
|
[qrMaxAliment]int{6, 30, 58, 86, 114, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{74, 46, 19},
|
||||||
|
{142, 114, 10},
|
||||||
|
{46, 16, 33},
|
||||||
|
{50, 22, 28},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 27
|
||||||
|
1828,
|
||||||
|
[qrMaxAliment]int{6, 34, 62, 90, 118, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{73, 45, 22},
|
||||||
|
{152, 122, 8},
|
||||||
|
{45, 15, 12},
|
||||||
|
{53, 23, 8},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 28
|
||||||
|
1921,
|
||||||
|
[qrMaxAliment]int{6, 26, 50, 74, 98, 122, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{73, 45, 3},
|
||||||
|
{147, 117, 3},
|
||||||
|
{45, 15, 11},
|
||||||
|
{54, 24, 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 29
|
||||||
|
2051,
|
||||||
|
[qrMaxAliment]int{6, 30, 54, 78, 102, 126, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{73, 45, 21},
|
||||||
|
{146, 116, 7},
|
||||||
|
{45, 15, 19},
|
||||||
|
{53, 23, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 30
|
||||||
|
2185,
|
||||||
|
[qrMaxAliment]int{6, 26, 52, 78, 104, 130, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{75, 47, 19},
|
||||||
|
{145, 115, 5},
|
||||||
|
{45, 15, 23},
|
||||||
|
{54, 24, 15},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 31
|
||||||
|
2323,
|
||||||
|
[qrMaxAliment]int{6, 30, 56, 82, 108, 134, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{74, 46, 2},
|
||||||
|
{145, 115, 13},
|
||||||
|
{45, 15, 23},
|
||||||
|
{54, 24, 42},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 32
|
||||||
|
2465,
|
||||||
|
[qrMaxAliment]int{6, 34, 60, 86, 112, 138, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{74, 46, 10},
|
||||||
|
{145, 115, 17},
|
||||||
|
{45, 15, 19},
|
||||||
|
{54, 24, 10},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 33
|
||||||
|
2611,
|
||||||
|
[qrMaxAliment]int{6, 30, 58, 86, 114, 142, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{74, 46, 14},
|
||||||
|
{145, 115, 17},
|
||||||
|
{45, 15, 11},
|
||||||
|
{54, 24, 29},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 34
|
||||||
|
2761,
|
||||||
|
[qrMaxAliment]int{6, 34, 62, 90, 118, 146, 0},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{74, 46, 14},
|
||||||
|
{145, 115, 13},
|
||||||
|
{46, 16, 59},
|
||||||
|
{54, 24, 44},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 35
|
||||||
|
2876,
|
||||||
|
[qrMaxAliment]int{6, 30, 54, 78, 102, 126, 150},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{75, 47, 12},
|
||||||
|
{151, 121, 12},
|
||||||
|
{45, 15, 22},
|
||||||
|
{54, 24, 39},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 36
|
||||||
|
3034,
|
||||||
|
[qrMaxAliment]int{6, 24, 50, 76, 102, 128, 154},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{75, 47, 6},
|
||||||
|
{151, 121, 6},
|
||||||
|
{45, 15, 2},
|
||||||
|
{54, 24, 46},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 37
|
||||||
|
3196,
|
||||||
|
[qrMaxAliment]int{6, 28, 54, 80, 106, 132, 158},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{74, 46, 29},
|
||||||
|
{152, 122, 17},
|
||||||
|
{45, 15, 24},
|
||||||
|
{54, 24, 49},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 38
|
||||||
|
3362,
|
||||||
|
[qrMaxAliment]int{6, 32, 58, 84, 110, 136, 162},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{74, 46, 13},
|
||||||
|
{152, 122, 4},
|
||||||
|
{45, 15, 42},
|
||||||
|
{54, 24, 48},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 39
|
||||||
|
3532,
|
||||||
|
[qrMaxAliment]int{6, 26, 54, 82, 110, 138, 166},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{75, 47, 40},
|
||||||
|
{147, 117, 20},
|
||||||
|
{45, 15, 10},
|
||||||
|
{54, 24, 43},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Version 40
|
||||||
|
3706,
|
||||||
|
[qrMaxAliment]int{6, 30, 58, 86, 114, 142, 170},
|
||||||
|
[4]qrRsParams{
|
||||||
|
{75, 47, 18},
|
||||||
|
{148, 118, 19},
|
||||||
|
{45, 15, 20},
|
||||||
|
{54, 24, 34},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
4
vendor/github.com/skip2/go-qrcode/.gitignore
generated
vendored
Normal file
4
vendor/github.com/skip2/go-qrcode/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.sw*
|
||||||
|
*.png
|
||||||
|
*.directory
|
||||||
|
qrcode/qrcode
|
||||||
8
vendor/github.com/skip2/go-qrcode/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/skip2/go-qrcode/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.7
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v ./...
|
||||||
|
|
||||||
19
vendor/github.com/skip2/go-qrcode/LICENSE
generated
vendored
Normal file
19
vendor/github.com/skip2/go-qrcode/LICENSE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2014 Tom Harwood
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
86
vendor/github.com/skip2/go-qrcode/README.md
generated
vendored
Normal file
86
vendor/github.com/skip2/go-qrcode/README.md
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# go-qrcode #
|
||||||
|
|
||||||
|
<img src='https://skip.org/img/nyancat-youtube-qr.png' align='right'>
|
||||||
|
|
||||||
|
Package qrcode implements a QR Code encoder. [](https://travis-ci.org/skip2/go-qrcode)
|
||||||
|
|
||||||
|
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be encoded, with URLs being a popular choice :)
|
||||||
|
|
||||||
|
Each QR Code contains error recovery information to aid reading damaged or obscured codes. There are four levels of error recovery: Low, medium, high and highest. QR Codes with a higher recovery level are more robust to damage, at the cost of being physically larger.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
go get -u github.com/skip2/go-qrcode/...
|
||||||
|
|
||||||
|
A command-line tool `qrcode` will be built into `$GOPATH/bin/`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
import qrcode "github.com/skip2/go-qrcode"
|
||||||
|
|
||||||
|
- **Create a 256x256 PNG image:**
|
||||||
|
|
||||||
|
var png []byte
|
||||||
|
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256)
|
||||||
|
|
||||||
|
- **Create a 256x256 PNG image and write to a file:**
|
||||||
|
|
||||||
|
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png")
|
||||||
|
|
||||||
|
- **Create a 256x256 PNG image with custom colors and write to file:**
|
||||||
|
|
||||||
|
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png")
|
||||||
|
|
||||||
|
All examples use the qrcode.Medium error Recovery Level and create a fixed 256x256px size QR Code. The last function creates a white on black instead of black on white QR Code.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/skip2/go-qrcode)
|
||||||
|
|
||||||
|
## Demoapp
|
||||||
|
|
||||||
|
[http://go-qrcode.appspot.com](http://go-qrcode.appspot.com)
|
||||||
|
|
||||||
|
## CLI
|
||||||
|
|
||||||
|
A command-line tool `qrcode` will be built into `$GOPATH/bin/`.
|
||||||
|
|
||||||
|
```
|
||||||
|
qrcode -- QR Code encoder in Go
|
||||||
|
https://github.com/skip2/go-qrcode
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-d disable QR Code border
|
||||||
|
-i invert black and white
|
||||||
|
-o string
|
||||||
|
out PNG file prefix, empty for stdout
|
||||||
|
-s int
|
||||||
|
image size (pixel) (default 256)
|
||||||
|
-t print as text-art on stdout
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
1. Arguments except for flags are joined by " " and used to generate QR code.
|
||||||
|
Default output is STDOUT, pipe to imagemagick command "display" to display
|
||||||
|
on any X server.
|
||||||
|
|
||||||
|
qrcode hello word | display
|
||||||
|
|
||||||
|
2. Save to file if "display" not available:
|
||||||
|
|
||||||
|
qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png
|
||||||
|
|
||||||
|
```
|
||||||
|
## Maximum capacity
|
||||||
|
The maximum capacity of a QR Code varies according to the content encoded and the error recovery level. The maximum capacity is 2,953 bytes, 4,296 alphanumeric characters, 7,089 numeric digits, or a combination of these.
|
||||||
|
|
||||||
|
## Borderless QR Codes
|
||||||
|
|
||||||
|
To aid QR Code reading software, QR codes have a built in whitespace border.
|
||||||
|
|
||||||
|
If you know what you're doing, and don't want a border, see https://gist.github.com/skip2/7e3d8a82f5317df9be437f8ec8ec0b7d for how to do it. It's still recommended you include a border manually.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- [http://en.wikipedia.org/wiki/QR_code](http://en.wikipedia.org/wiki/QR_code)
|
||||||
|
- [ISO/IEC 18004:2006](http://www.iso.org/iso/catalogue_detail.htm?csnumber=43655) - Main QR Code specification (approx CHF 198,00)<br>
|
||||||
|
- [https://github.com/qpliu/qrencode-go/](https://github.com/qpliu/qrencode-go/) - alternative Go QR encoding library based on [ZXing](https://github.com/zxing/zxing)
|
||||||
273
vendor/github.com/skip2/go-qrcode/bitset/bitset.go
generated
vendored
Normal file
273
vendor/github.com/skip2/go-qrcode/bitset/bitset.go
generated
vendored
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
// go-qrcode
|
||||||
|
// Copyright 2014 Tom Harwood
|
||||||
|
|
||||||
|
// Package bitset implements an append only bit array.
|
||||||
|
//
|
||||||
|
// To create a Bitset and append some bits:
|
||||||
|
// // Bitset Contents
|
||||||
|
// b := bitset.New() // {}
|
||||||
|
// b.AppendBools(true, true, false) // {1, 1, 0}
|
||||||
|
// b.AppendBools(true) // {1, 1, 0, 1}
|
||||||
|
// b.AppendValue(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0}
|
||||||
|
//
|
||||||
|
// To read values:
|
||||||
|
//
|
||||||
|
// len := b.Len() // 8
|
||||||
|
// v := b.At(0) // 1
|
||||||
|
// v = b.At(1) // 1
|
||||||
|
// v = b.At(2) // 0
|
||||||
|
// v = b.At(8) // 0
|
||||||
|
package bitset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
b0 = false
|
||||||
|
b1 = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bitset stores an array of bits.
|
||||||
|
type Bitset struct {
|
||||||
|
// The number of bits stored.
|
||||||
|
numBits int
|
||||||
|
|
||||||
|
// Storage for individual bits.
|
||||||
|
bits []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns an initialised Bitset with optional initial bits v.
|
||||||
|
func New(v ...bool) *Bitset {
|
||||||
|
b := &Bitset{numBits: 0, bits: make([]byte, 0)}
|
||||||
|
b.AppendBools(v...)
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a copy.
|
||||||
|
func Clone(from *Bitset) *Bitset {
|
||||||
|
return &Bitset{numBits: from.numBits, bits: from.bits[:]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substr returns a substring, consisting of the bits from indexes start to end.
|
||||||
|
func (b *Bitset) Substr(start int, end int) *Bitset {
|
||||||
|
if start > end || end > b.numBits {
|
||||||
|
log.Panicf("Out of range start=%d end=%d numBits=%d", start, end, b.numBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := New()
|
||||||
|
result.ensureCapacity(end - start)
|
||||||
|
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
if b.At(i) {
|
||||||
|
result.bits[result.numBits/8] |= 0x80 >> uint(result.numBits%8)
|
||||||
|
}
|
||||||
|
result.numBits++
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromBase2String constructs and returns a Bitset from a string. The string
|
||||||
|
// consists of '1', '0' or ' ' characters, e.g. "1010 0101". The '1' and '0'
|
||||||
|
// characters represent true/false bits respectively, and ' ' characters are
|
||||||
|
// ignored.
|
||||||
|
//
|
||||||
|
// The function panics if the input string contains other characters.
|
||||||
|
func NewFromBase2String(b2string string) *Bitset {
|
||||||
|
b := &Bitset{numBits: 0, bits: make([]byte, 0)}
|
||||||
|
|
||||||
|
for _, c := range b2string {
|
||||||
|
switch c {
|
||||||
|
case '1':
|
||||||
|
b.AppendBools(true)
|
||||||
|
case '0':
|
||||||
|
b.AppendBools(false)
|
||||||
|
case ' ':
|
||||||
|
default:
|
||||||
|
log.Panicf("Invalid char %c in NewFromBase2String", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendBytes appends a list of whole bytes.
|
||||||
|
func (b *Bitset) AppendBytes(data []byte) {
|
||||||
|
for _, d := range data {
|
||||||
|
b.AppendByte(d, 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendByte appends the numBits least significant bits from value.
|
||||||
|
func (b *Bitset) AppendByte(value byte, numBits int) {
|
||||||
|
b.ensureCapacity(numBits)
|
||||||
|
|
||||||
|
if numBits > 8 {
|
||||||
|
log.Panicf("numBits %d out of range 0-8", numBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := numBits - 1; i >= 0; i-- {
|
||||||
|
if value&(1<<uint(i)) != 0 {
|
||||||
|
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.numBits++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendUint32 appends the numBits least significant bits from value.
|
||||||
|
func (b *Bitset) AppendUint32(value uint32, numBits int) {
|
||||||
|
b.ensureCapacity(numBits)
|
||||||
|
|
||||||
|
if numBits > 32 {
|
||||||
|
log.Panicf("numBits %d out of range 0-32", numBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := numBits - 1; i >= 0; i-- {
|
||||||
|
if value&(1<<uint(i)) != 0 {
|
||||||
|
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.numBits++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureCapacity ensures the Bitset can store an additional |numBits|.
|
||||||
|
//
|
||||||
|
// The underlying array is expanded if necessary. To prevent frequent
|
||||||
|
// reallocation, expanding the underlying array at least doubles its capacity.
|
||||||
|
func (b *Bitset) ensureCapacity(numBits int) {
|
||||||
|
numBits += b.numBits
|
||||||
|
|
||||||
|
newNumBytes := numBits / 8
|
||||||
|
if numBits%8 != 0 {
|
||||||
|
newNumBytes++
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b.bits) >= newNumBytes {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.bits = append(b.bits, make([]byte, newNumBytes+2*len(b.bits))...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append bits copied from |other|.
|
||||||
|
//
|
||||||
|
// The new length is b.Len() + other.Len().
|
||||||
|
func (b *Bitset) Append(other *Bitset) {
|
||||||
|
b.ensureCapacity(other.numBits)
|
||||||
|
|
||||||
|
for i := 0; i < other.numBits; i++ {
|
||||||
|
if other.At(i) {
|
||||||
|
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
||||||
|
}
|
||||||
|
b.numBits++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendBools appends bits to the Bitset.
|
||||||
|
func (b *Bitset) AppendBools(bits ...bool) {
|
||||||
|
b.ensureCapacity(len(bits))
|
||||||
|
|
||||||
|
for _, v := range bits {
|
||||||
|
if v {
|
||||||
|
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
||||||
|
}
|
||||||
|
b.numBits++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendNumBools appends num bits of value value.
|
||||||
|
func (b *Bitset) AppendNumBools(num int, value bool) {
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
b.AppendBools(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human readable representation of the Bitset's contents.
|
||||||
|
func (b *Bitset) String() string {
|
||||||
|
var bitString string
|
||||||
|
for i := 0; i < b.numBits; i++ {
|
||||||
|
if (i % 8) == 0 {
|
||||||
|
bitString += " "
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 {
|
||||||
|
bitString += "1"
|
||||||
|
} else {
|
||||||
|
bitString += "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("numBits=%d, bits=%s", b.numBits, bitString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the length of the Bitset in bits.
|
||||||
|
func (b *Bitset) Len() int {
|
||||||
|
return b.numBits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bits returns the contents of the Bitset.
|
||||||
|
func (b *Bitset) Bits() []bool {
|
||||||
|
result := make([]bool, b.numBits)
|
||||||
|
|
||||||
|
var i int
|
||||||
|
for i = 0; i < b.numBits; i++ {
|
||||||
|
result[i] = (b.bits[i/8] & (0x80 >> byte(i%8))) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// At returns the value of the bit at |index|.
|
||||||
|
func (b *Bitset) At(index int) bool {
|
||||||
|
if index >= b.numBits {
|
||||||
|
log.Panicf("Index %d out of range", index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (b.bits[index/8] & (0x80 >> byte(index%8))) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the Bitset equals other.
|
||||||
|
func (b *Bitset) Equals(other *Bitset) bool {
|
||||||
|
if b.numBits != other.numBits {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(b.bits[0:b.numBits/8], other.bits[0:b.numBits/8]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 8 * (b.numBits / 8); i < b.numBits; i++ {
|
||||||
|
a := (b.bits[i/8] & (0x80 >> byte(i%8)))
|
||||||
|
b := (other.bits[i/8] & (0x80 >> byte(i%8)))
|
||||||
|
|
||||||
|
if a != b {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByteAt returns a byte consisting of upto 8 bits starting at index.
|
||||||
|
func (b *Bitset) ByteAt(index int) byte {
|
||||||
|
if index < 0 || index >= b.numBits {
|
||||||
|
log.Panicf("Index %d out of range", index)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result byte
|
||||||
|
|
||||||
|
for i := index; i < index+8 && i < b.numBits; i++ {
|
||||||
|
result <<= 1
|
||||||
|
if b.At(i) {
|
||||||
|
result |= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
486
vendor/github.com/skip2/go-qrcode/encoder.go
generated
vendored
Normal file
486
vendor/github.com/skip2/go-qrcode/encoder.go
generated
vendored
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
// go-qrcode
|
||||||
|
// Copyright 2014 Tom Harwood
|
||||||
|
|
||||||
|
package qrcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
bitset "github.com/skip2/go-qrcode/bitset"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Data encoding.
|
||||||
|
//
|
||||||
|
// The main data portion of a QR Code consists of one or more segments of data.
|
||||||
|
// A segment consists of:
|
||||||
|
//
|
||||||
|
// - The segment Data Mode: numeric, alphanumeric, or byte.
|
||||||
|
// - The length of segment in bits.
|
||||||
|
// - Encoded data.
|
||||||
|
//
|
||||||
|
// For example, the string "123ZZ#!#!" may be represented as:
|
||||||
|
//
|
||||||
|
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]
|
||||||
|
//
|
||||||
|
// Multiple data modes exist to minimise the size of encoded data. For example,
|
||||||
|
// 8-bit bytes require 8 bits to encode each, but base 10 numeric data can be
|
||||||
|
// encoded at a higher density of 3 numbers (e.g. 123) per 10 bits.
|
||||||
|
//
|
||||||
|
// Some data can be represented in multiple modes. Numeric data can be
|
||||||
|
// represented in all three modes, whereas alphanumeric data (e.g. 'A') can be
|
||||||
|
// represented in alphanumeric and byte mode.
|
||||||
|
//
|
||||||
|
// Starting a new segment (to use a different Data Mode) has a cost, the bits to
|
||||||
|
// state the new segment Data Mode and length. To minimise each QR Code's symbol
|
||||||
|
// size, an optimisation routine coalesces segment types where possible, to
|
||||||
|
// reduce the encoded data length.
|
||||||
|
//
|
||||||
|
// There are several other data modes available (e.g. Kanji mode) which are not
|
||||||
|
// implemented here.
|
||||||
|
|
||||||
|
// A segment encoding mode.
|
||||||
|
type dataMode uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Each dataMode is a subset of the subsequent dataMode:
|
||||||
|
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
|
||||||
|
//
|
||||||
|
// This ordering is important for determining which data modes a character can
|
||||||
|
// be encoded with. E.g. 'E' can be encoded in both dataModeAlphanumeric and
|
||||||
|
// dataModeByte.
|
||||||
|
dataModeNone dataMode = 1 << iota
|
||||||
|
dataModeNumeric
|
||||||
|
dataModeAlphanumeric
|
||||||
|
dataModeByte
|
||||||
|
)
|
||||||
|
|
||||||
|
// dataModeString returns d as a short printable string.
|
||||||
|
func dataModeString(d dataMode) string {
|
||||||
|
switch d {
|
||||||
|
case dataModeNone:
|
||||||
|
return "none"
|
||||||
|
case dataModeNumeric:
|
||||||
|
return "numeric"
|
||||||
|
case dataModeAlphanumeric:
|
||||||
|
return "alphanumeric"
|
||||||
|
case dataModeByte:
|
||||||
|
return "byte"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
type dataEncoderType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
dataEncoderType1To9 dataEncoderType = iota
|
||||||
|
dataEncoderType10To26
|
||||||
|
dataEncoderType27To40
|
||||||
|
)
|
||||||
|
|
||||||
|
// segment is a single segment of data.
|
||||||
|
type segment struct {
|
||||||
|
// Data Mode (e.g. numeric).
|
||||||
|
dataMode dataMode
|
||||||
|
|
||||||
|
// segment data (e.g. "abc").
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// A dataEncoder encodes data for a particular QR Code version.
|
||||||
|
type dataEncoder struct {
|
||||||
|
// Minimum & maximum versions supported.
|
||||||
|
minVersion int
|
||||||
|
maxVersion int
|
||||||
|
|
||||||
|
// Mode indicator bit sequences.
|
||||||
|
numericModeIndicator *bitset.Bitset
|
||||||
|
alphanumericModeIndicator *bitset.Bitset
|
||||||
|
byteModeIndicator *bitset.Bitset
|
||||||
|
|
||||||
|
// Character count lengths.
|
||||||
|
numNumericCharCountBits int
|
||||||
|
numAlphanumericCharCountBits int
|
||||||
|
numByteCharCountBits int
|
||||||
|
|
||||||
|
// The raw input data.
|
||||||
|
data []byte
|
||||||
|
|
||||||
|
// The data classified into unoptimised segments.
|
||||||
|
actual []segment
|
||||||
|
|
||||||
|
// The data classified into optimised segments.
|
||||||
|
optimised []segment
|
||||||
|
}
|
||||||
|
|
||||||
|
// newDataEncoder constructs a dataEncoder.
|
||||||
|
func newDataEncoder(t dataEncoderType) *dataEncoder {
|
||||||
|
d := &dataEncoder{}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case dataEncoderType1To9:
|
||||||
|
d = &dataEncoder{
|
||||||
|
minVersion: 1,
|
||||||
|
maxVersion: 9,
|
||||||
|
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
||||||
|
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
||||||
|
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
||||||
|
numNumericCharCountBits: 10,
|
||||||
|
numAlphanumericCharCountBits: 9,
|
||||||
|
numByteCharCountBits: 8,
|
||||||
|
}
|
||||||
|
case dataEncoderType10To26:
|
||||||
|
d = &dataEncoder{
|
||||||
|
minVersion: 10,
|
||||||
|
maxVersion: 26,
|
||||||
|
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
||||||
|
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
||||||
|
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
||||||
|
numNumericCharCountBits: 12,
|
||||||
|
numAlphanumericCharCountBits: 11,
|
||||||
|
numByteCharCountBits: 16,
|
||||||
|
}
|
||||||
|
case dataEncoderType27To40:
|
||||||
|
d = &dataEncoder{
|
||||||
|
minVersion: 27,
|
||||||
|
maxVersion: 40,
|
||||||
|
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
||||||
|
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
||||||
|
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
||||||
|
numNumericCharCountBits: 14,
|
||||||
|
numAlphanumericCharCountBits: 13,
|
||||||
|
numByteCharCountBits: 16,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Panic("Unknown dataEncoderType")
|
||||||
|
}
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode data as one or more segments and return the encoded data.
|
||||||
|
//
|
||||||
|
// The returned data does not include the terminator bit sequence.
|
||||||
|
func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) {
|
||||||
|
d.data = data
|
||||||
|
d.actual = nil
|
||||||
|
d.optimised = nil
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil, errors.New("no data to encode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Classify data into unoptimised segments.
|
||||||
|
highestRequiredMode := d.classifyDataModes()
|
||||||
|
|
||||||
|
// Optimise segments.
|
||||||
|
err := d.optimiseDataModes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a single byte encoded segment would be more efficient.
|
||||||
|
optimizedLength := 0
|
||||||
|
for _, s := range d.optimised {
|
||||||
|
length, err := d.encodedLength(s.dataMode, len(s.data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
optimizedLength += length
|
||||||
|
}
|
||||||
|
|
||||||
|
singleByteSegmentLength, err := d.encodedLength(highestRequiredMode, len(d.data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if singleByteSegmentLength <= optimizedLength {
|
||||||
|
d.optimised = []segment{segment{dataMode: highestRequiredMode, data: d.data}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode data.
|
||||||
|
encoded := bitset.New()
|
||||||
|
for _, s := range d.optimised {
|
||||||
|
d.encodeDataRaw(s.data, s.dataMode, encoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// classifyDataModes classifies the raw data into unoptimised segments.
|
||||||
|
// e.g. "123ZZ#!#!" =>
|
||||||
|
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"].
|
||||||
|
//
|
||||||
|
// Returns the highest data mode needed to encode the data. e.g. for a mixed
|
||||||
|
// numeric/alphanumeric input, the highest is alphanumeric.
|
||||||
|
//
|
||||||
|
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
|
||||||
|
func (d *dataEncoder) classifyDataModes() dataMode {
|
||||||
|
var start int
|
||||||
|
mode := dataModeNone
|
||||||
|
highestRequiredMode := mode
|
||||||
|
|
||||||
|
for i, v := range d.data {
|
||||||
|
newMode := dataModeNone
|
||||||
|
switch {
|
||||||
|
case v >= 0x30 && v <= 0x39:
|
||||||
|
newMode = dataModeNumeric
|
||||||
|
case v == 0x20 || v == 0x24 || v == 0x25 || v == 0x2a || v == 0x2b || v ==
|
||||||
|
0x2d || v == 0x2e || v == 0x2f || v == 0x3a || (v >= 0x41 && v <= 0x5a):
|
||||||
|
newMode = dataModeAlphanumeric
|
||||||
|
default:
|
||||||
|
newMode = dataModeByte
|
||||||
|
}
|
||||||
|
|
||||||
|
if newMode != mode {
|
||||||
|
if i > 0 {
|
||||||
|
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:i]})
|
||||||
|
|
||||||
|
start = i
|
||||||
|
}
|
||||||
|
|
||||||
|
mode = newMode
|
||||||
|
}
|
||||||
|
|
||||||
|
if newMode > highestRequiredMode {
|
||||||
|
highestRequiredMode = newMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]})
|
||||||
|
|
||||||
|
return highestRequiredMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// optimiseDataModes optimises the list of segments to reduce the overall output
|
||||||
|
// encoded data length.
|
||||||
|
//
|
||||||
|
// The algorithm coalesces adjacent segments. segments are only coalesced when
|
||||||
|
// the Data Modes are compatible, and when the coalesced segment has a shorter
|
||||||
|
// encoded length than separate segments.
|
||||||
|
//
|
||||||
|
// Multiple segments may be coalesced. For example a string of alternating
|
||||||
|
// alphanumeric/numeric segments ANANANANA can be optimised to just A.
|
||||||
|
func (d *dataEncoder) optimiseDataModes() error {
|
||||||
|
for i := 0; i < len(d.actual); {
|
||||||
|
mode := d.actual[i].dataMode
|
||||||
|
numChars := len(d.actual[i].data)
|
||||||
|
|
||||||
|
j := i + 1
|
||||||
|
for j < len(d.actual) {
|
||||||
|
nextNumChars := len(d.actual[j].data)
|
||||||
|
nextMode := d.actual[j].dataMode
|
||||||
|
|
||||||
|
if nextMode > mode {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
coalescedLength, err := d.encodedLength(mode, numChars+nextNumChars)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
seperateLength1, err := d.encodedLength(mode, numChars)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
seperateLength2, err := d.encodedLength(nextMode, nextNumChars)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if coalescedLength < seperateLength1+seperateLength2 {
|
||||||
|
j++
|
||||||
|
numChars += nextNumChars
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
optimised := segment{dataMode: mode,
|
||||||
|
data: make([]byte, 0, numChars)}
|
||||||
|
|
||||||
|
for k := i; k < j; k++ {
|
||||||
|
optimised.data = append(optimised.data, d.actual[k].data...)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.optimised = append(d.optimised, optimised)
|
||||||
|
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeDataRaw encodes data in dataMode. The encoded data is appended to
|
||||||
|
// encoded.
|
||||||
|
func (d *dataEncoder) encodeDataRaw(data []byte, dataMode dataMode, encoded *bitset.Bitset) {
|
||||||
|
modeIndicator := d.modeIndicator(dataMode)
|
||||||
|
charCountBits := d.charCountBits(dataMode)
|
||||||
|
|
||||||
|
// Append mode indicator.
|
||||||
|
encoded.Append(modeIndicator)
|
||||||
|
|
||||||
|
// Append character count.
|
||||||
|
encoded.AppendUint32(uint32(len(data)), charCountBits)
|
||||||
|
|
||||||
|
// Append data.
|
||||||
|
switch dataMode {
|
||||||
|
case dataModeNumeric:
|
||||||
|
for i := 0; i < len(data); i += 3 {
|
||||||
|
charsRemaining := len(data) - i
|
||||||
|
|
||||||
|
var value uint32
|
||||||
|
bitsUsed := 1
|
||||||
|
|
||||||
|
for j := 0; j < charsRemaining && j < 3; j++ {
|
||||||
|
value *= 10
|
||||||
|
value += uint32(data[i+j] - 0x30)
|
||||||
|
bitsUsed += 3
|
||||||
|
}
|
||||||
|
encoded.AppendUint32(value, bitsUsed)
|
||||||
|
}
|
||||||
|
case dataModeAlphanumeric:
|
||||||
|
for i := 0; i < len(data); i += 2 {
|
||||||
|
charsRemaining := len(data) - i
|
||||||
|
|
||||||
|
var value uint32
|
||||||
|
for j := 0; j < charsRemaining && j < 2; j++ {
|
||||||
|
value *= 45
|
||||||
|
value += encodeAlphanumericCharacter(data[i+j])
|
||||||
|
}
|
||||||
|
|
||||||
|
bitsUsed := 6
|
||||||
|
if charsRemaining > 1 {
|
||||||
|
bitsUsed = 11
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded.AppendUint32(value, bitsUsed)
|
||||||
|
}
|
||||||
|
case dataModeByte:
|
||||||
|
for _, b := range data {
|
||||||
|
encoded.AppendByte(b, 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// modeIndicator returns the segment header bits for a segment of type dataMode.
|
||||||
|
func (d *dataEncoder) modeIndicator(dataMode dataMode) *bitset.Bitset {
|
||||||
|
switch dataMode {
|
||||||
|
case dataModeNumeric:
|
||||||
|
return d.numericModeIndicator
|
||||||
|
case dataModeAlphanumeric:
|
||||||
|
return d.alphanumericModeIndicator
|
||||||
|
case dataModeByte:
|
||||||
|
return d.byteModeIndicator
|
||||||
|
default:
|
||||||
|
log.Panic("Unknown data mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// charCountBits returns the number of bits used to encode the length of a data
|
||||||
|
// segment of type dataMode.
|
||||||
|
func (d *dataEncoder) charCountBits(dataMode dataMode) int {
|
||||||
|
switch dataMode {
|
||||||
|
case dataModeNumeric:
|
||||||
|
return d.numNumericCharCountBits
|
||||||
|
case dataModeAlphanumeric:
|
||||||
|
return d.numAlphanumericCharCountBits
|
||||||
|
case dataModeByte:
|
||||||
|
return d.numByteCharCountBits
|
||||||
|
default:
|
||||||
|
log.Panic("Unknown data mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodedLength returns the number of bits required to encode n symbols in
|
||||||
|
// dataMode.
|
||||||
|
//
|
||||||
|
// The number of bits required is affected by:
|
||||||
|
// - QR code type - Mode Indicator length.
|
||||||
|
// - Data mode - number of bits used to represent data length.
|
||||||
|
// - Data mode - how the data is encoded.
|
||||||
|
// - Number of symbols encoded.
|
||||||
|
//
|
||||||
|
// An error is returned if the mode is not supported, or the length requested is
|
||||||
|
// too long to be represented.
|
||||||
|
func (d *dataEncoder) encodedLength(dataMode dataMode, n int) (int, error) {
|
||||||
|
modeIndicator := d.modeIndicator(dataMode)
|
||||||
|
charCountBits := d.charCountBits(dataMode)
|
||||||
|
|
||||||
|
if modeIndicator == nil {
|
||||||
|
return 0, errors.New("mode not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
maxLength := (1 << uint8(charCountBits)) - 1
|
||||||
|
|
||||||
|
if n > maxLength {
|
||||||
|
return 0, errors.New("length too long to be represented")
|
||||||
|
}
|
||||||
|
|
||||||
|
length := modeIndicator.Len() + charCountBits
|
||||||
|
|
||||||
|
switch dataMode {
|
||||||
|
case dataModeNumeric:
|
||||||
|
length += 10 * (n / 3)
|
||||||
|
|
||||||
|
if n%3 != 0 {
|
||||||
|
length += 1 + 3*(n%3)
|
||||||
|
}
|
||||||
|
case dataModeAlphanumeric:
|
||||||
|
length += 11 * (n / 2)
|
||||||
|
length += 6 * (n % 2)
|
||||||
|
case dataModeByte:
|
||||||
|
length += 8 * n
|
||||||
|
}
|
||||||
|
|
||||||
|
return length, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeAlphanumericChar returns the QR Code encoded value of v.
|
||||||
|
//
|
||||||
|
// v must be a QR Code defined alphanumeric character: 0-9, A-Z, SP, $%*+-./ or
|
||||||
|
// :. The characters are mapped to values in the range 0-44 respectively.
|
||||||
|
func encodeAlphanumericCharacter(v byte) uint32 {
|
||||||
|
c := uint32(v)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case c >= '0' && c <= '9':
|
||||||
|
// 0-9 encoded as 0-9.
|
||||||
|
return c - '0'
|
||||||
|
case c >= 'A' && c <= 'Z':
|
||||||
|
// A-Z encoded as 10-35.
|
||||||
|
return c - 'A' + 10
|
||||||
|
case c == ' ':
|
||||||
|
return 36
|
||||||
|
case c == '$':
|
||||||
|
return 37
|
||||||
|
case c == '%':
|
||||||
|
return 38
|
||||||
|
case c == '*':
|
||||||
|
return 39
|
||||||
|
case c == '+':
|
||||||
|
return 40
|
||||||
|
case c == '-':
|
||||||
|
return 41
|
||||||
|
case c == '.':
|
||||||
|
return 42
|
||||||
|
case c == '/':
|
||||||
|
return 43
|
||||||
|
case c == ':':
|
||||||
|
return 44
|
||||||
|
default:
|
||||||
|
log.Panicf("encodeAlphanumericCharacter() with non alphanumeric char %v.", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
608
vendor/github.com/skip2/go-qrcode/qrcode.go
generated
vendored
Normal file
608
vendor/github.com/skip2/go-qrcode/qrcode.go
generated
vendored
Normal file
@ -0,0 +1,608 @@
|
|||||||
|
// go-qrcode
|
||||||
|
// Copyright 2014 Tom Harwood
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package qrcode implements a QR Code encoder.
|
||||||
|
|
||||||
|
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be
|
||||||
|
encoded.
|
||||||
|
|
||||||
|
A QR Code contains error recovery information to aid reading damaged or
|
||||||
|
obscured codes. There are four levels of error recovery: qrcode.{Low, Medium,
|
||||||
|
High, Highest}. QR Codes with a higher recovery level are more robust to damage,
|
||||||
|
at the cost of being physically larger.
|
||||||
|
|
||||||
|
Three functions cover most use cases:
|
||||||
|
|
||||||
|
- Create a PNG image:
|
||||||
|
|
||||||
|
var png []byte
|
||||||
|
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256)
|
||||||
|
|
||||||
|
- Create a PNG image and write to a file:
|
||||||
|
|
||||||
|
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png")
|
||||||
|
|
||||||
|
- Create a PNG image with custom colors and write to file:
|
||||||
|
|
||||||
|
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png")
|
||||||
|
|
||||||
|
All examples use the qrcode.Medium error Recovery Level and create a fixed
|
||||||
|
256x256px size QR Code. The last function creates a white on black instead of black
|
||||||
|
on white QR Code.
|
||||||
|
|
||||||
|
To generate a variable sized image instead, specify a negative size (in place of
|
||||||
|
the 256 above), such as -4 or -5. Larger negative numbers create larger images:
|
||||||
|
A size of -5 sets each module (QR Code "pixel") to be 5px wide/high.
|
||||||
|
|
||||||
|
- Create a PNG image (variable size, with minimum white padding) and write to a file:
|
||||||
|
|
||||||
|
err := qrcode.WriteFile("https://example.org", qrcode.Medium, -5, "qr.png")
|
||||||
|
|
||||||
|
The maximum capacity of a QR Code varies according to the content encoded and
|
||||||
|
the error recovery level. The maximum capacity is 2,953 bytes, 4,296
|
||||||
|
alphanumeric characters, 7,089 numeric digits, or a combination of these.
|
||||||
|
|
||||||
|
This package implements a subset of QR Code 2005, as defined in ISO/IEC
|
||||||
|
18004:2006.
|
||||||
|
*/
|
||||||
|
package qrcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
bitset "github.com/skip2/go-qrcode/bitset"
|
||||||
|
reedsolomon "github.com/skip2/go-qrcode/reedsolomon"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encode a QR Code and return a raw PNG image.
|
||||||
|
//
|
||||||
|
// size is both the image width and height in pixels. If size is too small then
|
||||||
|
// a larger image is silently returned. Negative values for size cause a
|
||||||
|
// variable sized image to be returned: See the documentation for Image().
|
||||||
|
//
|
||||||
|
// To serve over HTTP, remember to send a Content-Type: image/png header.
|
||||||
|
func Encode(content string, level RecoveryLevel, size int) ([]byte, error) {
|
||||||
|
var q *QRCode
|
||||||
|
|
||||||
|
q, err := New(content, level)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.PNG(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFile encodes, then writes a QR Code to the given filename in PNG format.
|
||||||
|
//
|
||||||
|
// size is both the image width and height in pixels. If size is too small then
|
||||||
|
// a larger image is silently written. Negative values for size cause a variable
|
||||||
|
// sized image to be written: See the documentation for Image().
|
||||||
|
func WriteFile(content string, level RecoveryLevel, size int, filename string) error {
|
||||||
|
var q *QRCode
|
||||||
|
|
||||||
|
q, err := New(content, level)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.WriteFile(size, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteColorFile encodes, then writes a QR Code to the given filename in PNG format.
|
||||||
|
// With WriteColorFile you can also specify the colors you want to use.
|
||||||
|
//
|
||||||
|
// size is both the image width and height in pixels. If size is too small then
|
||||||
|
// a larger image is silently written. Negative values for size cause a variable
|
||||||
|
// sized image to be written: See the documentation for Image().
|
||||||
|
func WriteColorFile(content string, level RecoveryLevel, size int, background,
|
||||||
|
foreground color.Color, filename string) error {
|
||||||
|
|
||||||
|
var q *QRCode
|
||||||
|
|
||||||
|
q, err := New(content, level)
|
||||||
|
|
||||||
|
q.BackgroundColor = background
|
||||||
|
q.ForegroundColor = foreground
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.WriteFile(size, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A QRCode represents a valid encoded QRCode.
|
||||||
|
type QRCode struct {
|
||||||
|
// Original content encoded.
|
||||||
|
Content string
|
||||||
|
|
||||||
|
// QR Code type.
|
||||||
|
Level RecoveryLevel
|
||||||
|
VersionNumber int
|
||||||
|
|
||||||
|
// User settable drawing options.
|
||||||
|
ForegroundColor color.Color
|
||||||
|
BackgroundColor color.Color
|
||||||
|
|
||||||
|
// Disable the QR Code border.
|
||||||
|
DisableBorder bool
|
||||||
|
|
||||||
|
encoder *dataEncoder
|
||||||
|
version qrCodeVersion
|
||||||
|
|
||||||
|
data *bitset.Bitset
|
||||||
|
symbol *symbol
|
||||||
|
mask int
|
||||||
|
}
|
||||||
|
|
||||||
|
// New constructs a QRCode.
|
||||||
|
//
|
||||||
|
// var q *qrcode.QRCode
|
||||||
|
// q, err := qrcode.New("my content", qrcode.Medium)
|
||||||
|
//
|
||||||
|
// An error occurs if the content is too long.
|
||||||
|
func New(content string, level RecoveryLevel) (*QRCode, error) {
|
||||||
|
encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26,
|
||||||
|
dataEncoderType27To40}
|
||||||
|
|
||||||
|
var encoder *dataEncoder
|
||||||
|
var encoded *bitset.Bitset
|
||||||
|
var chosenVersion *qrCodeVersion
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for _, t := range encoders {
|
||||||
|
encoder = newDataEncoder(t)
|
||||||
|
encoded, err = encoder.encode([]byte(content))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len())
|
||||||
|
|
||||||
|
if chosenVersion != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if chosenVersion == nil {
|
||||||
|
return nil, errors.New("content too long to encode")
|
||||||
|
}
|
||||||
|
|
||||||
|
q := &QRCode{
|
||||||
|
Content: content,
|
||||||
|
|
||||||
|
Level: level,
|
||||||
|
VersionNumber: chosenVersion.version,
|
||||||
|
|
||||||
|
ForegroundColor: color.Black,
|
||||||
|
BackgroundColor: color.White,
|
||||||
|
|
||||||
|
encoder: encoder,
|
||||||
|
data: encoded,
|
||||||
|
version: *chosenVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
return q, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithForcedVersion constructs a QRCode of a specific version.
|
||||||
|
//
|
||||||
|
// var q *qrcode.QRCode
|
||||||
|
// q, err := qrcode.NewWithForcedVersion("my content", 25, qrcode.Medium)
|
||||||
|
//
|
||||||
|
// An error occurs in case of invalid version.
|
||||||
|
func NewWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) {
|
||||||
|
var encoder *dataEncoder
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case version >= 1 && version <= 9:
|
||||||
|
encoder = newDataEncoder(dataEncoderType1To9)
|
||||||
|
case version >= 10 && version <= 26:
|
||||||
|
encoder = newDataEncoder(dataEncoderType10To26)
|
||||||
|
case version >= 27 && version <= 40:
|
||||||
|
encoder = newDataEncoder(dataEncoderType27To40)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Invalid version %d (expected 1-40 inclusive)", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
var encoded *bitset.Bitset
|
||||||
|
encoded, err := encoder.encode([]byte(content))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
chosenVersion := getQRCodeVersion(level, version)
|
||||||
|
|
||||||
|
if chosenVersion == nil {
|
||||||
|
return nil, errors.New("cannot find QR Code version")
|
||||||
|
}
|
||||||
|
|
||||||
|
if encoded.Len() > chosenVersion.numDataBits() {
|
||||||
|
return nil, fmt.Errorf("Cannot encode QR code: content too large for fixed size QR Code version %d (encoded length is %d bits, maximum length is %d bits)",
|
||||||
|
version,
|
||||||
|
encoded.Len(),
|
||||||
|
chosenVersion.numDataBits())
|
||||||
|
}
|
||||||
|
|
||||||
|
q := &QRCode{
|
||||||
|
Content: content,
|
||||||
|
|
||||||
|
Level: level,
|
||||||
|
VersionNumber: chosenVersion.version,
|
||||||
|
|
||||||
|
ForegroundColor: color.Black,
|
||||||
|
BackgroundColor: color.White,
|
||||||
|
|
||||||
|
encoder: encoder,
|
||||||
|
data: encoded,
|
||||||
|
version: *chosenVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
return q, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitmap returns the QR Code as a 2D array of 1-bit pixels.
|
||||||
|
//
|
||||||
|
// bitmap[y][x] is true if the pixel at (x, y) is set.
|
||||||
|
//
|
||||||
|
// The bitmap includes the required "quiet zone" around the QR Code to aid
|
||||||
|
// decoding.
|
||||||
|
func (q *QRCode) Bitmap() [][]bool {
|
||||||
|
// Build QR code.
|
||||||
|
q.encode()
|
||||||
|
|
||||||
|
return q.symbol.bitmap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image returns the QR Code as an image.Image.
|
||||||
|
//
|
||||||
|
// A positive size sets a fixed image width and height (e.g. 256 yields an
|
||||||
|
// 256x256px image).
|
||||||
|
//
|
||||||
|
// Depending on the amount of data encoded, fixed size images can have different
|
||||||
|
// amounts of padding (white space around the QR Code). As an alternative, a
|
||||||
|
// variable sized image can be generated instead:
|
||||||
|
//
|
||||||
|
// A negative size causes a variable sized image to be returned. The image
|
||||||
|
// returned is the minimum size required for the QR Code. Choose a larger
|
||||||
|
// negative number to increase the scale of the image. e.g. a size of -5 causes
|
||||||
|
// each module (QR Code "pixel") to be 5px in size.
|
||||||
|
func (q *QRCode) Image(size int) image.Image {
|
||||||
|
// Build QR code.
|
||||||
|
q.encode()
|
||||||
|
|
||||||
|
// Minimum pixels (both width and height) required.
|
||||||
|
realSize := q.symbol.size
|
||||||
|
|
||||||
|
// Variable size support.
|
||||||
|
if size < 0 {
|
||||||
|
size = size * -1 * realSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual pixels available to draw the symbol. Automatically increase the
|
||||||
|
// image size if it's not large enough.
|
||||||
|
if size < realSize {
|
||||||
|
size = realSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output image.
|
||||||
|
rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}}
|
||||||
|
|
||||||
|
// Saves a few bytes to have them in this order
|
||||||
|
p := color.Palette([]color.Color{q.BackgroundColor, q.ForegroundColor})
|
||||||
|
img := image.NewPaletted(rect, p)
|
||||||
|
fgClr := uint8(img.Palette.Index(q.ForegroundColor))
|
||||||
|
|
||||||
|
// QR code bitmap.
|
||||||
|
bitmap := q.symbol.bitmap()
|
||||||
|
|
||||||
|
// Map each image pixel to the nearest QR code module.
|
||||||
|
modulesPerPixel := float64(realSize) / float64(size)
|
||||||
|
for y := 0; y < size; y++ {
|
||||||
|
y2 := int(float64(y) * modulesPerPixel)
|
||||||
|
for x := 0; x < size; x++ {
|
||||||
|
x2 := int(float64(x) * modulesPerPixel)
|
||||||
|
|
||||||
|
v := bitmap[y2][x2]
|
||||||
|
|
||||||
|
if v {
|
||||||
|
pos := img.PixOffset(x, y)
|
||||||
|
img.Pix[pos] = fgClr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
// PNG returns the QR Code as a PNG image.
|
||||||
|
//
|
||||||
|
// size is both the image width and height in pixels. If size is too small then
|
||||||
|
// a larger image is silently returned. Negative values for size cause a
|
||||||
|
// variable sized image to be returned: See the documentation for Image().
|
||||||
|
func (q *QRCode) PNG(size int) ([]byte, error) {
|
||||||
|
img := q.Image(size)
|
||||||
|
|
||||||
|
encoder := png.Encoder{CompressionLevel: png.BestCompression}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
err := encoder.Encode(&b, img)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the QR Code as a PNG image to io.Writer.
|
||||||
|
//
|
||||||
|
// size is both the image width and height in pixels. If size is too small then
|
||||||
|
// a larger image is silently written. Negative values for size cause a
|
||||||
|
// variable sized image to be written: See the documentation for Image().
|
||||||
|
func (q *QRCode) Write(size int, out io.Writer) error {
|
||||||
|
var png []byte
|
||||||
|
|
||||||
|
png, err := q.PNG(size)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = out.Write(png)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFile writes the QR Code as a PNG image to the specified file.
|
||||||
|
//
|
||||||
|
// size is both the image width and height in pixels. If size is too small then
|
||||||
|
// a larger image is silently written. Negative values for size cause a
|
||||||
|
// variable sized image to be written: See the documentation for Image().
|
||||||
|
func (q *QRCode) WriteFile(size int, filename string) error {
|
||||||
|
var png []byte
|
||||||
|
|
||||||
|
png, err := q.PNG(size)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(filename, png, os.FileMode(0644))
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode completes the steps required to encode the QR Code. These include
|
||||||
|
// adding the terminator bits and padding, splitting the data into blocks and
|
||||||
|
// applying the error correction, and selecting the best data mask.
|
||||||
|
func (q *QRCode) encode() {
|
||||||
|
numTerminatorBits := q.version.numTerminatorBitsRequired(q.data.Len())
|
||||||
|
|
||||||
|
q.addTerminatorBits(numTerminatorBits)
|
||||||
|
q.addPadding()
|
||||||
|
|
||||||
|
encoded := q.encodeBlocks()
|
||||||
|
|
||||||
|
const numMasks int = 8
|
||||||
|
penalty := 0
|
||||||
|
|
||||||
|
for mask := 0; mask < numMasks; mask++ {
|
||||||
|
var s *symbol
|
||||||
|
var err error
|
||||||
|
|
||||||
|
s, err = buildRegularSymbol(q.version, mask, encoded, !q.DisableBorder)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
numEmptyModules := s.numEmptyModules()
|
||||||
|
if numEmptyModules != 0 {
|
||||||
|
log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)",
|
||||||
|
numEmptyModules, q.VersionNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := s.penaltyScore()
|
||||||
|
|
||||||
|
//log.Printf("mask=%d p=%3d p1=%3d p2=%3d p3=%3d p4=%d\n", mask, p, s.penalty1(), s.penalty2(), s.penalty3(), s.penalty4())
|
||||||
|
|
||||||
|
if q.symbol == nil || p < penalty {
|
||||||
|
q.symbol = s
|
||||||
|
q.mask = mask
|
||||||
|
penalty = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addTerminatorBits adds final terminator bits to the encoded data.
|
||||||
|
//
|
||||||
|
// The number of terminator bits required is determined when the QR Code version
|
||||||
|
// is chosen (which itself depends on the length of the data encoded). The
|
||||||
|
// terminator bits are thus added after the QR Code version
|
||||||
|
// is chosen, rather than at the data encoding stage.
|
||||||
|
func (q *QRCode) addTerminatorBits(numTerminatorBits int) {
|
||||||
|
q.data.AppendNumBools(numTerminatorBits, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeBlocks takes the completed (terminated & padded) encoded data, splits
|
||||||
|
// the data into blocks (as specified by the QR Code version), applies error
|
||||||
|
// correction to each block, then interleaves the blocks together.
|
||||||
|
//
|
||||||
|
// The QR Code's final data sequence is returned.
|
||||||
|
func (q *QRCode) encodeBlocks() *bitset.Bitset {
|
||||||
|
// Split into blocks.
|
||||||
|
type dataBlock struct {
|
||||||
|
data *bitset.Bitset
|
||||||
|
ecStartOffset int
|
||||||
|
}
|
||||||
|
|
||||||
|
block := make([]dataBlock, q.version.numBlocks())
|
||||||
|
|
||||||
|
start := 0
|
||||||
|
end := 0
|
||||||
|
blockID := 0
|
||||||
|
|
||||||
|
for _, b := range q.version.block {
|
||||||
|
for j := 0; j < b.numBlocks; j++ {
|
||||||
|
start = end
|
||||||
|
end = start + b.numDataCodewords*8
|
||||||
|
|
||||||
|
// Apply error correction to each block.
|
||||||
|
numErrorCodewords := b.numCodewords - b.numDataCodewords
|
||||||
|
block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords)
|
||||||
|
block[blockID].ecStartOffset = end - start
|
||||||
|
|
||||||
|
blockID++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interleave the blocks.
|
||||||
|
|
||||||
|
result := bitset.New()
|
||||||
|
|
||||||
|
// Combine data blocks.
|
||||||
|
working := true
|
||||||
|
for i := 0; working; i += 8 {
|
||||||
|
working = false
|
||||||
|
|
||||||
|
for j, b := range block {
|
||||||
|
if i >= block[j].ecStartOffset {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Append(b.data.Substr(i, i+8))
|
||||||
|
|
||||||
|
working = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine error correction blocks.
|
||||||
|
working = true
|
||||||
|
for i := 0; working; i += 8 {
|
||||||
|
working = false
|
||||||
|
|
||||||
|
for j, b := range block {
|
||||||
|
offset := i + block[j].ecStartOffset
|
||||||
|
if offset >= block[j].data.Len() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Append(b.data.Substr(offset, offset+8))
|
||||||
|
|
||||||
|
working = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append remainder bits.
|
||||||
|
result.AppendNumBools(q.version.numRemainderBits, false)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// max returns the maximum of a and b.
|
||||||
|
func max(a int, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPadding pads the encoded data upto the full length required.
|
||||||
|
func (q *QRCode) addPadding() {
|
||||||
|
numDataBits := q.version.numDataBits()
|
||||||
|
|
||||||
|
if q.data.Len() == numDataBits {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad to the nearest codeword boundary.
|
||||||
|
q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false)
|
||||||
|
|
||||||
|
// Pad codewords 0b11101100 and 0b00010001.
|
||||||
|
padding := [2]*bitset.Bitset{
|
||||||
|
bitset.New(true, true, true, false, true, true, false, false),
|
||||||
|
bitset.New(false, false, false, true, false, false, false, true),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert pad codewords alternately.
|
||||||
|
i := 0
|
||||||
|
for numDataBits-q.data.Len() >= 8 {
|
||||||
|
q.data.Append(padding[i])
|
||||||
|
|
||||||
|
i = 1 - i // Alternate between 0 and 1.
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.data.Len() != numDataBits {
|
||||||
|
log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToString produces a multi-line string that forms a QR-code image.
|
||||||
|
func (q *QRCode) ToString(inverseColor bool) string {
|
||||||
|
bits := q.Bitmap()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for y := range bits {
|
||||||
|
for x := range bits[y] {
|
||||||
|
if bits[y][x] != inverseColor {
|
||||||
|
buf.WriteString(" ")
|
||||||
|
} else {
|
||||||
|
buf.WriteString("██")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSmallString produces a multi-line string that forms a QR-code image, a
|
||||||
|
// factor two smaller in x and y then ToString.
|
||||||
|
func (q *QRCode) ToSmallString(inverseColor bool) string {
|
||||||
|
bits := q.Bitmap()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
// if there is an odd number of rows, the last one needs special treatment
|
||||||
|
for y := 0; y < len(bits)-1; y += 2 {
|
||||||
|
for x := range bits[y] {
|
||||||
|
if bits[y][x] == bits[y+1][x] {
|
||||||
|
if bits[y][x] != inverseColor {
|
||||||
|
buf.WriteString(" ")
|
||||||
|
} else {
|
||||||
|
buf.WriteString("█")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if bits[y][x] != inverseColor {
|
||||||
|
buf.WriteString("▄")
|
||||||
|
} else {
|
||||||
|
buf.WriteString("▀")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
// special treatment for the last row if odd
|
||||||
|
if len(bits)%2 == 1 {
|
||||||
|
y := len(bits) - 1
|
||||||
|
for x := range bits[y] {
|
||||||
|
if bits[y][x] != inverseColor {
|
||||||
|
buf.WriteString(" ")
|
||||||
|
} else {
|
||||||
|
buf.WriteString("▀")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
387
vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
generated
vendored
Normal file
387
vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
generated
vendored
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
// go-qrcode
|
||||||
|
// Copyright 2014 Tom Harwood
|
||||||
|
|
||||||
|
package reedsolomon
|
||||||
|
|
||||||
|
// Addition, subtraction, multiplication, and division in GF(2^8).
|
||||||
|
// Operations are performed modulo x^8 + x^4 + x^3 + x^2 + 1.
|
||||||
|
|
||||||
|
// http://en.wikipedia.org/wiki/Finite_field_arithmetic
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
const (
|
||||||
|
gfZero = gfElement(0)
|
||||||
|
gfOne = gfElement(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
gfExpTable = [256]gfElement{
|
||||||
|
/* 0 - 9 */ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58,
|
||||||
|
/* 10 - 19 */ 116, 232, 205, 135, 19, 38, 76, 152, 45, 90,
|
||||||
|
/* 20 - 29 */ 180, 117, 234, 201, 143, 3, 6, 12, 24, 48,
|
||||||
|
/* 30 - 39 */ 96, 192, 157, 39, 78, 156, 37, 74, 148, 53,
|
||||||
|
/* 40 - 49 */ 106, 212, 181, 119, 238, 193, 159, 35, 70, 140,
|
||||||
|
/* 50 - 59 */ 5, 10, 20, 40, 80, 160, 93, 186, 105, 210,
|
||||||
|
/* 60 - 69 */ 185, 111, 222, 161, 95, 190, 97, 194, 153, 47,
|
||||||
|
/* 70 - 79 */ 94, 188, 101, 202, 137, 15, 30, 60, 120, 240,
|
||||||
|
/* 80 - 89 */ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225,
|
||||||
|
/* 90 - 99 */ 223, 163, 91, 182, 113, 226, 217, 175, 67, 134,
|
||||||
|
/* 100 - 109 */ 17, 34, 68, 136, 13, 26, 52, 104, 208, 189,
|
||||||
|
/* 110 - 119 */ 103, 206, 129, 31, 62, 124, 248, 237, 199, 147,
|
||||||
|
/* 120 - 129 */ 59, 118, 236, 197, 151, 51, 102, 204, 133, 23,
|
||||||
|
/* 130 - 139 */ 46, 92, 184, 109, 218, 169, 79, 158, 33, 66,
|
||||||
|
/* 140 - 149 */ 132, 21, 42, 84, 168, 77, 154, 41, 82, 164,
|
||||||
|
/* 150 - 159 */ 85, 170, 73, 146, 57, 114, 228, 213, 183, 115,
|
||||||
|
/* 160 - 169 */ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229,
|
||||||
|
/* 170 - 179 */ 215, 179, 123, 246, 241, 255, 227, 219, 171, 75,
|
||||||
|
/* 180 - 189 */ 150, 49, 98, 196, 149, 55, 110, 220, 165, 87,
|
||||||
|
/* 190 - 199 */ 174, 65, 130, 25, 50, 100, 200, 141, 7, 14,
|
||||||
|
/* 200 - 209 */ 28, 56, 112, 224, 221, 167, 83, 166, 81, 162,
|
||||||
|
/* 210 - 219 */ 89, 178, 121, 242, 249, 239, 195, 155, 43, 86,
|
||||||
|
/* 220 - 229 */ 172, 69, 138, 9, 18, 36, 72, 144, 61, 122,
|
||||||
|
/* 230 - 239 */ 244, 245, 247, 243, 251, 235, 203, 139, 11, 22,
|
||||||
|
/* 240 - 249 */ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54,
|
||||||
|
/* 250 - 255 */ 108, 216, 173, 71, 142, 1}
|
||||||
|
|
||||||
|
gfLogTable = [256]int{
|
||||||
|
/* 0 - 9 */ -1, 0, 1, 25, 2, 50, 26, 198, 3, 223,
|
||||||
|
/* 10 - 19 */ 51, 238, 27, 104, 199, 75, 4, 100, 224, 14,
|
||||||
|
/* 20 - 29 */ 52, 141, 239, 129, 28, 193, 105, 248, 200, 8,
|
||||||
|
/* 30 - 39 */ 76, 113, 5, 138, 101, 47, 225, 36, 15, 33,
|
||||||
|
/* 40 - 49 */ 53, 147, 142, 218, 240, 18, 130, 69, 29, 181,
|
||||||
|
/* 50 - 59 */ 194, 125, 106, 39, 249, 185, 201, 154, 9, 120,
|
||||||
|
/* 60 - 69 */ 77, 228, 114, 166, 6, 191, 139, 98, 102, 221,
|
||||||
|
/* 70 - 79 */ 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
|
||||||
|
/* 80 - 89 */ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210,
|
||||||
|
/* 90 - 99 */ 19, 92, 131, 56, 70, 64, 30, 66, 182, 163,
|
||||||
|
/* 100 - 109 */ 195, 72, 126, 110, 107, 58, 40, 84, 250, 133,
|
||||||
|
/* 110 - 119 */ 186, 61, 202, 94, 155, 159, 10, 21, 121, 43,
|
||||||
|
/* 120 - 129 */ 78, 212, 229, 172, 115, 243, 167, 87, 7, 112,
|
||||||
|
/* 130 - 139 */ 192, 247, 140, 128, 99, 13, 103, 74, 222, 237,
|
||||||
|
/* 140 - 149 */ 49, 197, 254, 24, 227, 165, 153, 119, 38, 184,
|
||||||
|
/* 150 - 159 */ 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
|
||||||
|
/* 160 - 169 */ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135,
|
||||||
|
/* 170 - 179 */ 151, 178, 220, 252, 190, 97, 242, 86, 211, 171,
|
||||||
|
/* 180 - 189 */ 20, 42, 93, 158, 132, 60, 57, 83, 71, 109,
|
||||||
|
/* 190 - 199 */ 65, 162, 31, 45, 67, 216, 183, 123, 164, 118,
|
||||||
|
/* 200 - 209 */ 196, 23, 73, 236, 127, 12, 111, 246, 108, 161,
|
||||||
|
/* 210 - 219 */ 59, 82, 41, 157, 85, 170, 251, 96, 134, 177,
|
||||||
|
/* 220 - 229 */ 187, 204, 62, 90, 203, 89, 95, 176, 156, 169,
|
||||||
|
/* 230 - 239 */ 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
|
||||||
|
/* 240 - 249 */ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214,
|
||||||
|
/* 250 - 255 */ 244, 234, 168, 80, 88, 175}
|
||||||
|
)
|
||||||
|
|
||||||
|
// gfElement is an element in GF(2^8).
|
||||||
|
type gfElement uint8
|
||||||
|
|
||||||
|
// newGFElement creates and returns a new gfElement.
|
||||||
|
func newGFElement(data byte) gfElement {
|
||||||
|
return gfElement(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gfAdd returns a + b.
|
||||||
|
func gfAdd(a, b gfElement) gfElement {
|
||||||
|
return a ^ b
|
||||||
|
}
|
||||||
|
|
||||||
|
// gfSub returns a - b.
|
||||||
|
//
|
||||||
|
// Note addition is equivalent to subtraction in GF(2).
|
||||||
|
func gfSub(a, b gfElement) gfElement {
|
||||||
|
return a ^ b
|
||||||
|
}
|
||||||
|
|
||||||
|
// gfMultiply returns a * b.
|
||||||
|
func gfMultiply(a, b gfElement) gfElement {
|
||||||
|
if a == gfZero || b == gfZero {
|
||||||
|
return gfZero
|
||||||
|
}
|
||||||
|
|
||||||
|
return gfExpTable[(gfLogTable[a]+gfLogTable[b])%255]
|
||||||
|
}
|
||||||
|
|
||||||
|
// gfDivide returns a / b.
|
||||||
|
//
|
||||||
|
// Divide by zero results in a panic.
|
||||||
|
func gfDivide(a, b gfElement) gfElement {
|
||||||
|
if a == gfZero {
|
||||||
|
return gfZero
|
||||||
|
} else if b == gfZero {
|
||||||
|
log.Panicln("Divide by zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
return gfMultiply(a, gfInverse(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// gfInverse returns the multiplicative inverse of a, a^-1.
|
||||||
|
//
|
||||||
|
// a * a^-1 = 1
|
||||||
|
func gfInverse(a gfElement) gfElement {
|
||||||
|
if a == gfZero {
|
||||||
|
log.Panicln("No multiplicative inverse of 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
return gfExpTable[255-gfLogTable[a]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// a^i | bits | polynomial | decimal
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// 0 | 000000000 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 0
|
||||||
|
// a^0 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1
|
||||||
|
// a^1 | 000000010 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 2
|
||||||
|
// a^2 | 000000100 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 4
|
||||||
|
// a^3 | 000001000 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 8
|
||||||
|
// a^4 | 000010000 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 16
|
||||||
|
// a^5 | 000100000 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 32
|
||||||
|
// a^6 | 001000000 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 64
|
||||||
|
// a^7 | 010000000 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 128
|
||||||
|
// a^8 | 000011101 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 29
|
||||||
|
// a^9 | 000111010 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 58
|
||||||
|
// a^10 | 001110100 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 116
|
||||||
|
// a^11 | 011101000 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 232
|
||||||
|
// a^12 | 011001101 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 205
|
||||||
|
// a^13 | 010000111 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 135
|
||||||
|
// a^14 | 000010011 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 19
|
||||||
|
// a^15 | 000100110 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 38
|
||||||
|
// a^16 | 001001100 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 76
|
||||||
|
// a^17 | 010011000 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 152
|
||||||
|
// a^18 | 000101101 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 45
|
||||||
|
// a^19 | 001011010 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 90
|
||||||
|
// a^20 | 010110100 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 180
|
||||||
|
// a^21 | 001110101 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 117
|
||||||
|
// a^22 | 011101010 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 234
|
||||||
|
// a^23 | 011001001 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 201
|
||||||
|
// a^24 | 010001111 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 143
|
||||||
|
// a^25 | 000000011 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 3
|
||||||
|
// a^26 | 000000110 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 6
|
||||||
|
// a^27 | 000001100 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 12
|
||||||
|
// a^28 | 000011000 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 24
|
||||||
|
// a^29 | 000110000 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 48
|
||||||
|
// a^30 | 001100000 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 96
|
||||||
|
// a^31 | 011000000 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 192
|
||||||
|
// a^32 | 010011101 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 157
|
||||||
|
// a^33 | 000100111 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 39
|
||||||
|
// a^34 | 001001110 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 78
|
||||||
|
// a^35 | 010011100 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 156
|
||||||
|
// a^36 | 000100101 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 37
|
||||||
|
// a^37 | 001001010 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 74
|
||||||
|
// a^38 | 010010100 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 148
|
||||||
|
// a^39 | 000110101 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 53
|
||||||
|
// a^40 | 001101010 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 106
|
||||||
|
// a^41 | 011010100 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 212
|
||||||
|
// a^42 | 010110101 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 181
|
||||||
|
// a^43 | 001110111 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 119
|
||||||
|
// a^44 | 011101110 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 238
|
||||||
|
// a^45 | 011000001 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 193
|
||||||
|
// a^46 | 010011111 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 159
|
||||||
|
// a^47 | 000100011 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 35
|
||||||
|
// a^48 | 001000110 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 70
|
||||||
|
// a^49 | 010001100 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 140
|
||||||
|
// a^50 | 000000101 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 5
|
||||||
|
// a^51 | 000001010 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 10
|
||||||
|
// a^52 | 000010100 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 20
|
||||||
|
// a^53 | 000101000 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 40
|
||||||
|
// a^54 | 001010000 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 80
|
||||||
|
// a^55 | 010100000 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 160
|
||||||
|
// a^56 | 001011101 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 93
|
||||||
|
// a^57 | 010111010 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 186
|
||||||
|
// a^58 | 001101001 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 105
|
||||||
|
// a^59 | 011010010 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 210
|
||||||
|
// a^60 | 010111001 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 185
|
||||||
|
// a^61 | 001101111 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 111
|
||||||
|
// a^62 | 011011110 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 222
|
||||||
|
// a^63 | 010100001 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 161
|
||||||
|
// a^64 | 001011111 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 95
|
||||||
|
// a^65 | 010111110 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 190
|
||||||
|
// a^66 | 001100001 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 97
|
||||||
|
// a^67 | 011000010 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 194
|
||||||
|
// a^68 | 010011001 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 153
|
||||||
|
// a^69 | 000101111 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 47
|
||||||
|
// a^70 | 001011110 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 94
|
||||||
|
// a^71 | 010111100 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 188
|
||||||
|
// a^72 | 001100101 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 101
|
||||||
|
// a^73 | 011001010 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 202
|
||||||
|
// a^74 | 010001001 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 137
|
||||||
|
// a^75 | 000001111 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 15
|
||||||
|
// a^76 | 000011110 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 30
|
||||||
|
// a^77 | 000111100 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 60
|
||||||
|
// a^78 | 001111000 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 120
|
||||||
|
// a^79 | 011110000 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 240
|
||||||
|
// a^80 | 011111101 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 253
|
||||||
|
// a^81 | 011100111 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 231
|
||||||
|
// a^82 | 011010011 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 211
|
||||||
|
// a^83 | 010111011 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 187
|
||||||
|
// a^84 | 001101011 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 107
|
||||||
|
// a^85 | 011010110 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 214
|
||||||
|
// a^86 | 010110001 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 177
|
||||||
|
// a^87 | 001111111 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 127
|
||||||
|
// a^88 | 011111110 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 254
|
||||||
|
// a^89 | 011100001 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 225
|
||||||
|
// a^90 | 011011111 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 223
|
||||||
|
// a^91 | 010100011 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 163
|
||||||
|
// a^92 | 001011011 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 91
|
||||||
|
// a^93 | 010110110 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 182
|
||||||
|
// a^94 | 001110001 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 113
|
||||||
|
// a^95 | 011100010 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 226
|
||||||
|
// a^96 | 011011001 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 217
|
||||||
|
// a^97 | 010101111 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 175
|
||||||
|
// a^98 | 001000011 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 67
|
||||||
|
// a^99 | 010000110 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 134
|
||||||
|
// a^100 | 000010001 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 17
|
||||||
|
// a^101 | 000100010 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 34
|
||||||
|
// a^102 | 001000100 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 68
|
||||||
|
// a^103 | 010001000 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 136
|
||||||
|
// a^104 | 000001101 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 13
|
||||||
|
// a^105 | 000011010 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 26
|
||||||
|
// a^106 | 000110100 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 52
|
||||||
|
// a^107 | 001101000 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 104
|
||||||
|
// a^108 | 011010000 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 208
|
||||||
|
// a^109 | 010111101 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 189
|
||||||
|
// a^110 | 001100111 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 103
|
||||||
|
// a^111 | 011001110 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 206
|
||||||
|
// a^112 | 010000001 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 129
|
||||||
|
// a^113 | 000011111 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 31
|
||||||
|
// a^114 | 000111110 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 62
|
||||||
|
// a^115 | 001111100 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 124
|
||||||
|
// a^116 | 011111000 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 248
|
||||||
|
// a^117 | 011101101 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 237
|
||||||
|
// a^118 | 011000111 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 199
|
||||||
|
// a^119 | 010010011 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 147
|
||||||
|
// a^120 | 000111011 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 59
|
||||||
|
// a^121 | 001110110 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 118
|
||||||
|
// a^122 | 011101100 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 236
|
||||||
|
// a^123 | 011000101 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 197
|
||||||
|
// a^124 | 010010111 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 151
|
||||||
|
// a^125 | 000110011 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 51
|
||||||
|
// a^126 | 001100110 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 102
|
||||||
|
// a^127 | 011001100 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 204
|
||||||
|
// a^128 | 010000101 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 133
|
||||||
|
// a^129 | 000010111 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 23
|
||||||
|
// a^130 | 000101110 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 46
|
||||||
|
// a^131 | 001011100 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 92
|
||||||
|
// a^132 | 010111000 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 184
|
||||||
|
// a^133 | 001101101 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 109
|
||||||
|
// a^134 | 011011010 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 218
|
||||||
|
// a^135 | 010101001 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 169
|
||||||
|
// a^136 | 001001111 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 79
|
||||||
|
// a^137 | 010011110 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 158
|
||||||
|
// a^138 | 000100001 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 33
|
||||||
|
// a^139 | 001000010 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 66
|
||||||
|
// a^140 | 010000100 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 132
|
||||||
|
// a^141 | 000010101 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 21
|
||||||
|
// a^142 | 000101010 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 42
|
||||||
|
// a^143 | 001010100 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 84
|
||||||
|
// a^144 | 010101000 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 168
|
||||||
|
// a^145 | 001001101 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 77
|
||||||
|
// a^146 | 010011010 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 154
|
||||||
|
// a^147 | 000101001 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 41
|
||||||
|
// a^148 | 001010010 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 82
|
||||||
|
// a^149 | 010100100 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 164
|
||||||
|
// a^150 | 001010101 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 85
|
||||||
|
// a^151 | 010101010 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 170
|
||||||
|
// a^152 | 001001001 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 73
|
||||||
|
// a^153 | 010010010 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 146
|
||||||
|
// a^154 | 000111001 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 57
|
||||||
|
// a^155 | 001110010 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 114
|
||||||
|
// a^156 | 011100100 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 228
|
||||||
|
// a^157 | 011010101 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 213
|
||||||
|
// a^158 | 010110111 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 183
|
||||||
|
// a^159 | 001110011 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 115
|
||||||
|
// a^160 | 011100110 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 230
|
||||||
|
// a^161 | 011010001 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 209
|
||||||
|
// a^162 | 010111111 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 191
|
||||||
|
// a^163 | 001100011 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 99
|
||||||
|
// a^164 | 011000110 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 198
|
||||||
|
// a^165 | 010010001 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 145
|
||||||
|
// a^166 | 000111111 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 63
|
||||||
|
// a^167 | 001111110 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 126
|
||||||
|
// a^168 | 011111100 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 252
|
||||||
|
// a^169 | 011100101 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 229
|
||||||
|
// a^170 | 011010111 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 215
|
||||||
|
// a^171 | 010110011 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 179
|
||||||
|
// a^172 | 001111011 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 123
|
||||||
|
// a^173 | 011110110 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 246
|
||||||
|
// a^174 | 011110001 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 241
|
||||||
|
// a^175 | 011111111 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 255
|
||||||
|
// a^176 | 011100011 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 227
|
||||||
|
// a^177 | 011011011 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 219
|
||||||
|
// a^178 | 010101011 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 171
|
||||||
|
// a^179 | 001001011 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 75
|
||||||
|
// a^180 | 010010110 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 150
|
||||||
|
// a^181 | 000110001 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 49
|
||||||
|
// a^182 | 001100010 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 98
|
||||||
|
// a^183 | 011000100 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 196
|
||||||
|
// a^184 | 010010101 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 149
|
||||||
|
// a^185 | 000110111 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 55
|
||||||
|
// a^186 | 001101110 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 110
|
||||||
|
// a^187 | 011011100 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 220
|
||||||
|
// a^188 | 010100101 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 165
|
||||||
|
// a^189 | 001010111 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 87
|
||||||
|
// a^190 | 010101110 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 174
|
||||||
|
// a^191 | 001000001 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 65
|
||||||
|
// a^192 | 010000010 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 130
|
||||||
|
// a^193 | 000011001 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 25
|
||||||
|
// a^194 | 000110010 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 50
|
||||||
|
// a^195 | 001100100 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 100
|
||||||
|
// a^196 | 011001000 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 200
|
||||||
|
// a^197 | 010001101 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 141
|
||||||
|
// a^198 | 000000111 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 7
|
||||||
|
// a^199 | 000001110 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 14
|
||||||
|
// a^200 | 000011100 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 28
|
||||||
|
// a^201 | 000111000 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 56
|
||||||
|
// a^202 | 001110000 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 112
|
||||||
|
// a^203 | 011100000 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 224
|
||||||
|
// a^204 | 011011101 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 221
|
||||||
|
// a^205 | 010100111 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 167
|
||||||
|
// a^206 | 001010011 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 83
|
||||||
|
// a^207 | 010100110 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 166
|
||||||
|
// a^208 | 001010001 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 81
|
||||||
|
// a^209 | 010100010 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 162
|
||||||
|
// a^210 | 001011001 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 89
|
||||||
|
// a^211 | 010110010 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 178
|
||||||
|
// a^212 | 001111001 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 121
|
||||||
|
// a^213 | 011110010 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 242
|
||||||
|
// a^214 | 011111001 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 249
|
||||||
|
// a^215 | 011101111 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 239
|
||||||
|
// a^216 | 011000011 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 195
|
||||||
|
// a^217 | 010011011 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 155
|
||||||
|
// a^218 | 000101011 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 43
|
||||||
|
// a^219 | 001010110 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 86
|
||||||
|
// a^220 | 010101100 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 172
|
||||||
|
// a^221 | 001000101 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 69
|
||||||
|
// a^222 | 010001010 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 138
|
||||||
|
// a^223 | 000001001 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 9
|
||||||
|
// a^224 | 000010010 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 18
|
||||||
|
// a^225 | 000100100 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 36
|
||||||
|
// a^226 | 001001000 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 72
|
||||||
|
// a^227 | 010010000 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 144
|
||||||
|
// a^228 | 000111101 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 61
|
||||||
|
// a^229 | 001111010 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 122
|
||||||
|
// a^230 | 011110100 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 244
|
||||||
|
// a^231 | 011110101 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 245
|
||||||
|
// a^232 | 011110111 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 247
|
||||||
|
// a^233 | 011110011 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 243
|
||||||
|
// a^234 | 011111011 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 251
|
||||||
|
// a^235 | 011101011 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 235
|
||||||
|
// a^236 | 011001011 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 203
|
||||||
|
// a^237 | 010001011 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 139
|
||||||
|
// a^238 | 000001011 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 11
|
||||||
|
// a^239 | 000010110 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 22
|
||||||
|
// a^240 | 000101100 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 44
|
||||||
|
// a^241 | 001011000 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 88
|
||||||
|
// a^242 | 010110000 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 176
|
||||||
|
// a^243 | 001111101 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 125
|
||||||
|
// a^244 | 011111010 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 250
|
||||||
|
// a^245 | 011101001 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 233
|
||||||
|
// a^246 | 011001111 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 207
|
||||||
|
// a^247 | 010000011 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 131
|
||||||
|
// a^248 | 000011011 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 27
|
||||||
|
// a^249 | 000110110 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 54
|
||||||
|
// a^250 | 001101100 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 108
|
||||||
|
// a^251 | 011011000 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 216
|
||||||
|
// a^252 | 010101101 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 173
|
||||||
|
// a^253 | 001000111 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 71
|
||||||
|
// a^254 | 010001110 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 142
|
||||||
|
// a^255 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1
|
||||||
216
vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
generated
vendored
Normal file
216
vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
generated
vendored
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
// go-qrcode
|
||||||
|
// Copyright 2014 Tom Harwood
|
||||||
|
|
||||||
|
package reedsolomon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
bitset "github.com/skip2/go-qrcode/bitset"
|
||||||
|
)
|
||||||
|
|
||||||
|
// gfPoly is a polynomial over GF(2^8).
|
||||||
|
type gfPoly struct {
|
||||||
|
// The ith value is the coefficient of the ith degree of x.
|
||||||
|
// term[0]*(x^0) + term[1]*(x^1) + term[2]*(x^2) ...
|
||||||
|
term []gfElement
|
||||||
|
}
|
||||||
|
|
||||||
|
// newGFPolyFromData returns |data| as a polynomial over GF(2^8).
|
||||||
|
//
|
||||||
|
// Each data byte becomes the coefficient of an x term.
|
||||||
|
//
|
||||||
|
// For an n byte input the polynomial is:
|
||||||
|
// data[n-1]*(x^n-1) + data[n-2]*(x^n-2) ... + data[0]*(x^0).
|
||||||
|
func newGFPolyFromData(data *bitset.Bitset) gfPoly {
|
||||||
|
numTotalBytes := data.Len() / 8
|
||||||
|
if data.Len()%8 != 0 {
|
||||||
|
numTotalBytes++
|
||||||
|
}
|
||||||
|
|
||||||
|
result := gfPoly{term: make([]gfElement, numTotalBytes)}
|
||||||
|
|
||||||
|
i := numTotalBytes - 1
|
||||||
|
for j := 0; j < data.Len(); j += 8 {
|
||||||
|
result.term[i] = gfElement(data.ByteAt(j))
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// newGFPolyMonomial returns term*(x^degree).
|
||||||
|
func newGFPolyMonomial(term gfElement, degree int) gfPoly {
|
||||||
|
if term == gfZero {
|
||||||
|
return gfPoly{}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := gfPoly{term: make([]gfElement, degree+1)}
|
||||||
|
result.term[degree] = term
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e gfPoly) data(numTerms int) []byte {
|
||||||
|
result := make([]byte, numTerms)
|
||||||
|
|
||||||
|
i := numTerms - len(e.term)
|
||||||
|
for j := len(e.term) - 1; j >= 0; j-- {
|
||||||
|
result[i] = byte(e.term[j])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// numTerms returns the number of
|
||||||
|
func (e gfPoly) numTerms() int {
|
||||||
|
return len(e.term)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gfPolyMultiply returns a * b.
|
||||||
|
func gfPolyMultiply(a, b gfPoly) gfPoly {
|
||||||
|
numATerms := a.numTerms()
|
||||||
|
numBTerms := b.numTerms()
|
||||||
|
|
||||||
|
result := gfPoly{term: make([]gfElement, numATerms+numBTerms)}
|
||||||
|
|
||||||
|
for i := 0; i < numATerms; i++ {
|
||||||
|
for j := 0; j < numBTerms; j++ {
|
||||||
|
if a.term[i] != 0 && b.term[j] != 0 {
|
||||||
|
monomial := gfPoly{term: make([]gfElement, i+j+1)}
|
||||||
|
monomial.term[i+j] = gfMultiply(a.term[i], b.term[j])
|
||||||
|
|
||||||
|
result = gfPolyAdd(result, monomial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.normalised()
|
||||||
|
}
|
||||||
|
|
||||||
|
// gfPolyRemainder return the remainder of numerator / denominator.
|
||||||
|
func gfPolyRemainder(numerator, denominator gfPoly) gfPoly {
|
||||||
|
if denominator.equals(gfPoly{}) {
|
||||||
|
log.Panicln("Remainder by zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
remainder := numerator
|
||||||
|
|
||||||
|
for remainder.numTerms() >= denominator.numTerms() {
|
||||||
|
degree := remainder.numTerms() - denominator.numTerms()
|
||||||
|
coefficient := gfDivide(remainder.term[remainder.numTerms()-1],
|
||||||
|
denominator.term[denominator.numTerms()-1])
|
||||||
|
|
||||||
|
divisor := gfPolyMultiply(denominator,
|
||||||
|
newGFPolyMonomial(coefficient, degree))
|
||||||
|
|
||||||
|
remainder = gfPolyAdd(remainder, divisor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remainder.normalised()
|
||||||
|
}
|
||||||
|
|
||||||
|
// gfPolyAdd returns a + b.
|
||||||
|
func gfPolyAdd(a, b gfPoly) gfPoly {
|
||||||
|
numATerms := a.numTerms()
|
||||||
|
numBTerms := b.numTerms()
|
||||||
|
|
||||||
|
numTerms := numATerms
|
||||||
|
if numBTerms > numTerms {
|
||||||
|
numTerms = numBTerms
|
||||||
|
}
|
||||||
|
|
||||||
|
result := gfPoly{term: make([]gfElement, numTerms)}
|
||||||
|
|
||||||
|
for i := 0; i < numTerms; i++ {
|
||||||
|
switch {
|
||||||
|
case numATerms > i && numBTerms > i:
|
||||||
|
result.term[i] = gfAdd(a.term[i], b.term[i])
|
||||||
|
case numATerms > i:
|
||||||
|
result.term[i] = a.term[i]
|
||||||
|
default:
|
||||||
|
result.term[i] = b.term[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.normalised()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e gfPoly) normalised() gfPoly {
|
||||||
|
numTerms := e.numTerms()
|
||||||
|
maxNonzeroTerm := numTerms - 1
|
||||||
|
|
||||||
|
for i := numTerms - 1; i >= 0; i-- {
|
||||||
|
if e.term[i] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
maxNonzeroTerm = i - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxNonzeroTerm < 0 {
|
||||||
|
return gfPoly{}
|
||||||
|
} else if maxNonzeroTerm < numTerms-1 {
|
||||||
|
e.term = e.term[0 : maxNonzeroTerm+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e gfPoly) string(useIndexForm bool) string {
|
||||||
|
var str string
|
||||||
|
numTerms := e.numTerms()
|
||||||
|
|
||||||
|
for i := numTerms - 1; i >= 0; i-- {
|
||||||
|
if e.term[i] > 0 {
|
||||||
|
if len(str) > 0 {
|
||||||
|
str += " + "
|
||||||
|
}
|
||||||
|
|
||||||
|
if !useIndexForm {
|
||||||
|
str += fmt.Sprintf("%dx^%d", e.term[i], i)
|
||||||
|
} else {
|
||||||
|
str += fmt.Sprintf("a^%dx^%d", gfLogTable[e.term[i]], i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str) == 0 {
|
||||||
|
str = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// equals returns true if e == other.
|
||||||
|
func (e gfPoly) equals(other gfPoly) bool {
|
||||||
|
var minecPoly *gfPoly
|
||||||
|
var maxecPoly *gfPoly
|
||||||
|
|
||||||
|
if e.numTerms() > other.numTerms() {
|
||||||
|
minecPoly = &other
|
||||||
|
maxecPoly = &e
|
||||||
|
} else {
|
||||||
|
minecPoly = &e
|
||||||
|
maxecPoly = &other
|
||||||
|
}
|
||||||
|
|
||||||
|
numMinTerms := minecPoly.numTerms()
|
||||||
|
numMaxTerms := maxecPoly.numTerms()
|
||||||
|
|
||||||
|
for i := 0; i < numMinTerms; i++ {
|
||||||
|
if e.term[i] != other.term[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := numMinTerms; i < numMaxTerms; i++ {
|
||||||
|
if maxecPoly.term[i] != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
73
vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
generated
vendored
Normal file
73
vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// go-qrcode
|
||||||
|
// Copyright 2014 Tom Harwood
|
||||||
|
|
||||||
|
// Package reedsolomon provides error correction encoding for QR Code 2005.
|
||||||
|
//
|
||||||
|
// QR Code 2005 uses a Reed-Solomon error correcting code to detect and correct
|
||||||
|
// errors encountered during decoding.
|
||||||
|
//
|
||||||
|
// The generated RS codes are systematic, and consist of the input data with
|
||||||
|
// error correction bytes appended.
|
||||||
|
package reedsolomon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
bitset "github.com/skip2/go-qrcode/bitset"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encode data for QR Code 2005 using the appropriate Reed-Solomon code.
|
||||||
|
//
|
||||||
|
// numECBytes is the number of error correction bytes to append, and is
|
||||||
|
// determined by the target QR Code's version and error correction level.
|
||||||
|
//
|
||||||
|
// ISO/IEC 18004 table 9 specifies the numECBytes required. e.g. a 1-L code has
|
||||||
|
// numECBytes=7.
|
||||||
|
func Encode(data *bitset.Bitset, numECBytes int) *bitset.Bitset {
|
||||||
|
// Create a polynomial representing |data|.
|
||||||
|
//
|
||||||
|
// The bytes are interpreted as the sequence of coefficients of a polynomial.
|
||||||
|
// The last byte's value becomes the x^0 coefficient, the second to last
|
||||||
|
// becomes the x^1 coefficient and so on.
|
||||||
|
ecpoly := newGFPolyFromData(data)
|
||||||
|
ecpoly = gfPolyMultiply(ecpoly, newGFPolyMonomial(gfOne, numECBytes))
|
||||||
|
|
||||||
|
// Pick the generator polynomial.
|
||||||
|
generator := rsGeneratorPoly(numECBytes)
|
||||||
|
|
||||||
|
// Generate the error correction bytes.
|
||||||
|
remainder := gfPolyRemainder(ecpoly, generator)
|
||||||
|
|
||||||
|
// Combine the data & error correcting bytes.
|
||||||
|
// The mathematically correct answer is:
|
||||||
|
//
|
||||||
|
// result := gfPolyAdd(ecpoly, remainder).
|
||||||
|
//
|
||||||
|
// The encoding used by QR Code 2005 is slightly different this result: To
|
||||||
|
// preserve the original |data| bit sequence exactly, the data and remainder
|
||||||
|
// are combined manually below. This ensures any most significant zero bits
|
||||||
|
// are preserved (and not optimised away).
|
||||||
|
result := bitset.Clone(data)
|
||||||
|
result.AppendBytes(remainder.data(numECBytes))
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// rsGeneratorPoly returns the Reed-Solomon generator polynomial with |degree|.
|
||||||
|
//
|
||||||
|
// The generator polynomial is calculated as:
|
||||||
|
// (x + a^0)(x + a^1)...(x + a^degree-1)
|
||||||
|
func rsGeneratorPoly(degree int) gfPoly {
|
||||||
|
if degree < 2 {
|
||||||
|
log.Panic("degree < 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
generator := gfPoly{term: []gfElement{1}}
|
||||||
|
|
||||||
|
for i := 0; i < degree; i++ {
|
||||||
|
nextPoly := gfPoly{term: []gfElement{gfExpTable[i], 1}}
|
||||||
|
generator = gfPolyMultiply(generator, nextPoly)
|
||||||
|
}
|
||||||
|
|
||||||
|
return generator
|
||||||
|
}
|
||||||
315
vendor/github.com/skip2/go-qrcode/regular_symbol.go
generated
vendored
Normal file
315
vendor/github.com/skip2/go-qrcode/regular_symbol.go
generated
vendored
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
// 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, includeQuietZone bool) (*symbol, error) {
|
||||||
|
|
||||||
|
quietZoneSize := 0
|
||||||
|
if includeQuietZone {
|
||||||
|
quietZoneSize = version.quietZoneSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
m := ®ularSymbol{
|
||||||
|
version: version,
|
||||||
|
mask: mask,
|
||||||
|
data: data,
|
||||||
|
|
||||||
|
symbol: newSymbol(version.symbolSize(), 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
|
||||||
|
}
|
||||||
309
vendor/github.com/skip2/go-qrcode/symbol.go
generated
vendored
Normal file
309
vendor/github.com/skip2/go-qrcode/symbol.go
generated
vendored
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
// go-qrcode
|
||||||
|
// Copyright 2014 Tom Harwood
|
||||||
|
|
||||||
|
package qrcode
|
||||||
|
|
||||||
|
// symbol is a 2D array of bits representing a QR Code symbol.
|
||||||
|
//
|
||||||
|
// A symbol consists of size*size modules, with each module normally drawn as a
|
||||||
|
// black or white square. The symbol also has a border of quietZoneSize modules.
|
||||||
|
//
|
||||||
|
// A (fictional) size=2, quietZoneSize=1 QR Code looks like:
|
||||||
|
//
|
||||||
|
// +----+
|
||||||
|
// | |
|
||||||
|
// | ab |
|
||||||
|
// | cd |
|
||||||
|
// | |
|
||||||
|
// +----+
|
||||||
|
//
|
||||||
|
// For ease of implementation, the functions to set/get bits ignore the border,
|
||||||
|
// so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the
|
||||||
|
// border) is returned by bitmap().
|
||||||
|
//
|
||||||
|
type symbol struct {
|
||||||
|
// Value of module at [y][x]. True is set.
|
||||||
|
module [][]bool
|
||||||
|
|
||||||
|
// True if the module at [y][x] is used (to either true or false).
|
||||||
|
// Used to identify unused modules.
|
||||||
|
isUsed [][]bool
|
||||||
|
|
||||||
|
// Combined width/height of the symbol and quiet zones.
|
||||||
|
//
|
||||||
|
// size = symbolSize + 2*quietZoneSize.
|
||||||
|
size int
|
||||||
|
|
||||||
|
// Width/height of the symbol only.
|
||||||
|
symbolSize int
|
||||||
|
|
||||||
|
// Width/height of a single quiet zone.
|
||||||
|
quietZoneSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSymbol constructs a symbol of size size*size, with a border of
|
||||||
|
// quietZoneSize.
|
||||||
|
func newSymbol(size int, quietZoneSize int) *symbol {
|
||||||
|
var m symbol
|
||||||
|
|
||||||
|
m.module = make([][]bool, size+2*quietZoneSize)
|
||||||
|
m.isUsed = make([][]bool, size+2*quietZoneSize)
|
||||||
|
|
||||||
|
for i := range m.module {
|
||||||
|
m.module[i] = make([]bool, size+2*quietZoneSize)
|
||||||
|
m.isUsed[i] = make([]bool, size+2*quietZoneSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.size = size + 2*quietZoneSize
|
||||||
|
m.symbolSize = size
|
||||||
|
m.quietZoneSize = quietZoneSize
|
||||||
|
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
// get returns the module value at (x, y).
|
||||||
|
func (m *symbol) get(x int, y int) (v bool) {
|
||||||
|
v = m.module[y+m.quietZoneSize][x+m.quietZoneSize]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty returns true if the module at (x, y) has not been set (to either true
|
||||||
|
// or false).
|
||||||
|
func (m *symbol) empty(x int, y int) bool {
|
||||||
|
return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize]
|
||||||
|
}
|
||||||
|
|
||||||
|
// numEmptyModules returns the number of empty modules.
|
||||||
|
//
|
||||||
|
// Initially numEmptyModules is symbolSize * symbolSize. After every module has
|
||||||
|
// been set (to either true or false), the number of empty modules is zero.
|
||||||
|
func (m *symbol) numEmptyModules() int {
|
||||||
|
var count int
|
||||||
|
for y := 0; y < m.symbolSize; y++ {
|
||||||
|
for x := 0; x < m.symbolSize; x++ {
|
||||||
|
if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sets the module at (x, y) to v.
|
||||||
|
func (m *symbol) set(x int, y int, v bool) {
|
||||||
|
m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v
|
||||||
|
m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// set2dPattern sets a 2D array of modules, starting at (x, y).
|
||||||
|
func (m *symbol) set2dPattern(x int, y int, v [][]bool) {
|
||||||
|
for j, row := range v {
|
||||||
|
for i, value := range row {
|
||||||
|
m.set(x+i, y+j, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitmap returns the entire symbol, including the quiet zone.
|
||||||
|
func (m *symbol) bitmap() [][]bool {
|
||||||
|
module := make([][]bool, len(m.module))
|
||||||
|
|
||||||
|
for i := range m.module {
|
||||||
|
module[i] = m.module[i][:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return module
|
||||||
|
}
|
||||||
|
|
||||||
|
// string returns a pictorial representation of the symbol, suitable for
|
||||||
|
// printing in a TTY.
|
||||||
|
func (m *symbol) string() string {
|
||||||
|
var result string
|
||||||
|
|
||||||
|
for _, row := range m.module {
|
||||||
|
for _, value := range row {
|
||||||
|
switch value {
|
||||||
|
case true:
|
||||||
|
result += " "
|
||||||
|
case false:
|
||||||
|
// Unicode 'FULL BLOCK' (U+2588).
|
||||||
|
result += "██"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants used to weight penalty calculations. Specified by ISO/IEC
|
||||||
|
// 18004:2006.
|
||||||
|
const (
|
||||||
|
penaltyWeight1 = 3
|
||||||
|
penaltyWeight2 = 3
|
||||||
|
penaltyWeight3 = 40
|
||||||
|
penaltyWeight4 = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// penaltyScore returns the penalty score of the symbol. The penalty score
|
||||||
|
// consists of the sum of the four individual penalty types.
|
||||||
|
func (m *symbol) penaltyScore() int {
|
||||||
|
return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4()
|
||||||
|
}
|
||||||
|
|
||||||
|
// penalty1 returns the penalty score for "adjacent modules in row/column with
|
||||||
|
// same colour".
|
||||||
|
//
|
||||||
|
// The numbers of adjacent matching modules and scores are:
|
||||||
|
// 0-5: score = 0
|
||||||
|
// 6+ : score = penaltyWeight1 + (numAdjacentModules - 5)
|
||||||
|
func (m *symbol) penalty1() int {
|
||||||
|
penalty := 0
|
||||||
|
|
||||||
|
for x := 0; x < m.symbolSize; x++ {
|
||||||
|
lastValue := m.get(x, 0)
|
||||||
|
count := 1
|
||||||
|
|
||||||
|
for y := 1; y < m.symbolSize; y++ {
|
||||||
|
v := m.get(x, y)
|
||||||
|
|
||||||
|
if v != lastValue {
|
||||||
|
count = 1
|
||||||
|
lastValue = v
|
||||||
|
} else {
|
||||||
|
count++
|
||||||
|
if count == 6 {
|
||||||
|
penalty += penaltyWeight1 + 1
|
||||||
|
} else if count > 6 {
|
||||||
|
penalty++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := 0; y < m.symbolSize; y++ {
|
||||||
|
lastValue := m.get(0, y)
|
||||||
|
count := 1
|
||||||
|
|
||||||
|
for x := 1; x < m.symbolSize; x++ {
|
||||||
|
v := m.get(x, y)
|
||||||
|
|
||||||
|
if v != lastValue {
|
||||||
|
count = 1
|
||||||
|
lastValue = v
|
||||||
|
} else {
|
||||||
|
count++
|
||||||
|
if count == 6 {
|
||||||
|
penalty += penaltyWeight1 + 1
|
||||||
|
} else if count > 6 {
|
||||||
|
penalty++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return penalty
|
||||||
|
}
|
||||||
|
|
||||||
|
// penalty2 returns the penalty score for "block of modules in the same colour".
|
||||||
|
//
|
||||||
|
// m*n: score = penaltyWeight2 * (m-1) * (n-1).
|
||||||
|
func (m *symbol) penalty2() int {
|
||||||
|
penalty := 0
|
||||||
|
|
||||||
|
for y := 1; y < m.symbolSize; y++ {
|
||||||
|
for x := 1; x < m.symbolSize; x++ {
|
||||||
|
topLeft := m.get(x-1, y-1)
|
||||||
|
above := m.get(x, y-1)
|
||||||
|
left := m.get(x-1, y)
|
||||||
|
current := m.get(x, y)
|
||||||
|
|
||||||
|
if current == left && current == above && current == topLeft {
|
||||||
|
penalty++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return penalty * penaltyWeight2
|
||||||
|
}
|
||||||
|
|
||||||
|
// penalty3 returns the penalty score for "1:1:3:1:1 ratio
|
||||||
|
// (dark:light:dark:light:dark) pattern in row/column, preceded or followed by
|
||||||
|
// light area 4 modules wide".
|
||||||
|
//
|
||||||
|
// Existence of the pattern scores penaltyWeight3.
|
||||||
|
func (m *symbol) penalty3() int {
|
||||||
|
penalty := 0
|
||||||
|
|
||||||
|
for y := 0; y < m.symbolSize; y++ {
|
||||||
|
var bitBuffer int16 = 0x00
|
||||||
|
|
||||||
|
for x := 0; x < m.symbolSize; x++ {
|
||||||
|
bitBuffer <<= 1
|
||||||
|
if v := m.get(x, y); v {
|
||||||
|
bitBuffer |= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
switch bitBuffer & 0x7ff {
|
||||||
|
// 0b000 0101 1101 or 0b10111010000
|
||||||
|
// 0x05d or 0x5d0
|
||||||
|
case 0x05d, 0x5d0:
|
||||||
|
penalty += penaltyWeight3
|
||||||
|
bitBuffer = 0xFF
|
||||||
|
default:
|
||||||
|
if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
|
||||||
|
penalty += penaltyWeight3
|
||||||
|
bitBuffer = 0xFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for x := 0; x < m.symbolSize; x++ {
|
||||||
|
var bitBuffer int16 = 0x00
|
||||||
|
|
||||||
|
for y := 0; y < m.symbolSize; y++ {
|
||||||
|
bitBuffer <<= 1
|
||||||
|
if v := m.get(x, y); v {
|
||||||
|
bitBuffer |= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
switch bitBuffer & 0x7ff {
|
||||||
|
// 0b000 0101 1101 or 0b10111010000
|
||||||
|
// 0x05d or 0x5d0
|
||||||
|
case 0x05d, 0x5d0:
|
||||||
|
penalty += penaltyWeight3
|
||||||
|
bitBuffer = 0xFF
|
||||||
|
default:
|
||||||
|
if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
|
||||||
|
penalty += penaltyWeight3
|
||||||
|
bitBuffer = 0xFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return penalty
|
||||||
|
}
|
||||||
|
|
||||||
|
// penalty4 returns the penalty score...
|
||||||
|
func (m *symbol) penalty4() int {
|
||||||
|
numModules := m.symbolSize * m.symbolSize
|
||||||
|
numDarkModules := 0
|
||||||
|
|
||||||
|
for x := 0; x < m.symbolSize; x++ {
|
||||||
|
for y := 0; y < m.symbolSize; y++ {
|
||||||
|
if v := m.get(x, y); v {
|
||||||
|
numDarkModules++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numDarkModuleDeviation := numModules/2 - numDarkModules
|
||||||
|
if numDarkModuleDeviation < 0 {
|
||||||
|
numDarkModuleDeviation *= -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20))
|
||||||
|
}
|
||||||
3050
vendor/github.com/skip2/go-qrcode/version.go
generated
vendored
Normal file
3050
vendor/github.com/skip2/go-qrcode/version.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
@ -135,6 +135,9 @@ github.com/jeandeaual/go-locale
|
|||||||
github.com/jsummers/gobmp
|
github.com/jsummers/gobmp
|
||||||
# github.com/kr/text v0.2.0
|
# github.com/kr/text v0.2.0
|
||||||
## explicit
|
## explicit
|
||||||
|
# github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea
|
||||||
|
## explicit; go 1.12
|
||||||
|
github.com/liyue201/goqr
|
||||||
# github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
# github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
## explicit
|
## explicit
|
||||||
github.com/nfnt/resize
|
github.com/nfnt/resize
|
||||||
@ -158,6 +161,11 @@ github.com/rymdport/portal/notification
|
|||||||
github.com/rymdport/portal/openuri
|
github.com/rymdport/portal/openuri
|
||||||
github.com/rymdport/portal/settings
|
github.com/rymdport/portal/settings
|
||||||
github.com/rymdport/portal/settings/appearance
|
github.com/rymdport/portal/settings/appearance
|
||||||
|
# github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
|
## explicit; go 1.13
|
||||||
|
github.com/skip2/go-qrcode
|
||||||
|
github.com/skip2/go-qrcode/bitset
|
||||||
|
github.com/skip2/go-qrcode/reedsolomon
|
||||||
# github.com/spf13/cobra v1.10.1
|
# github.com/spf13/cobra v1.10.1
|
||||||
## explicit; go 1.15
|
## explicit; go 1.15
|
||||||
github.com/spf13/cobra
|
github.com/spf13/cobra
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user