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/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // 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/nicksnyder/go-i18n/v2 v2.5.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // 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/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // 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/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/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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
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/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
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/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
|
||||
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/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
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/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/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/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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 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/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/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"
|
||||
encrypt "fckeuspy-go/lib"
|
||||
"image/color"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@ -11,30 +12,57 @@ import (
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/storage"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
type uiParts struct {
|
||||
outKey *widget.Entry
|
||||
msg *widget.Entry
|
||||
peer *widget.Entry
|
||||
cipherOut *widget.Entry
|
||||
payload *widget.Entry
|
||||
plainOut *widget.Entry
|
||||
toastLabel *widget.Label
|
||||
outKey *widget.Entry
|
||||
msg *widget.Entry
|
||||
peer *widget.Entry
|
||||
cipherOut *widget.Entry
|
||||
payload *widget.Entry
|
||||
plainOut *widget.Entry
|
||||
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 {
|
||||
p := &uiParts{
|
||||
outKey: widget.NewMultiLineEntry(),
|
||||
msg: widget.NewMultiLineEntry(),
|
||||
peer: widget.NewMultiLineEntry(),
|
||||
cipherOut: widget.NewMultiLineEntry(),
|
||||
payload: widget.NewMultiLineEntry(),
|
||||
plainOut: widget.NewMultiLineEntry(),
|
||||
toastLabel: widget.NewLabel(""),
|
||||
outKey: widget.NewMultiLineEntry(),
|
||||
msg: widget.NewMultiLineEntry(),
|
||||
peer: widget.NewMultiLineEntry(),
|
||||
cipherOut: widget.NewMultiLineEntry(),
|
||||
payload: widget.NewMultiLineEntry(),
|
||||
plainOut: widget.NewMultiLineEntry(),
|
||||
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.msg.SetPlaceHolder("Sem napiš zprávu…")
|
||||
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()) })
|
||||
|
||||
deleteBtn := widget.NewButton("Smazat identitu", func() {
|
||||
// dialog pro heslo
|
||||
pwEntry := widget.NewPasswordEntry()
|
||||
pwEntry.SetPlaceHolder("Heslo pro potvrzení…")
|
||||
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()
|
||||
return
|
||||
}
|
||||
// Quit app po odstranění (uživatel musí znovu spustit a vytvořit nový vault)
|
||||
fyne.CurrentApp().Quit()
|
||||
}, 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(
|
||||
widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
tileIdentity,
|
||||
parts.outKey,
|
||||
identityContainer,
|
||||
)
|
||||
return container.NewVScroll(group)
|
||||
}
|
||||
@ -148,11 +230,71 @@ func copyClip(s string, parts *uiParts) {
|
||||
// assembleResponsive builds split view that collapses for narrow widths
|
||||
// Tab: Encrypt
|
||||
func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
parts.cipherOut.Disable() // read only output
|
||||
parts.cipherOut.Disable()
|
||||
peerBtns := buttonTile(
|
||||
widget.NewButton("Paste", func() { parts.peer.SetText(fyne.CurrentApp().Clipboard().Content()) }),
|
||||
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() {
|
||||
m := parts.msg.Text
|
||||
p := parts.peer.Text
|
||||
@ -163,23 +305,74 @@ func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
go func(msg, peer string) {
|
||||
res, err := svc.Encrypt(msg, peer)
|
||||
if err != nil {
|
||||
fyne.Do(func() { parts.cipherOut.SetText(""); parts.showToast("Chyba") })
|
||||
fyne.Do(func() { parts.cipherOut.SetText(""); updateQR(""); parts.showToast("Chyba") })
|
||||
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)
|
||||
}
|
||||
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(
|
||||
widget.NewButton("Clear+Paste", func() { parts.msg.SetText(""); parts.msg.SetText(fyne.CurrentApp().Clipboard().Content()) }),
|
||||
widget.NewButton("Encrypt", encAction),
|
||||
widget.NewButton("Copy encrypted", func() { copyClip(parts.cipherOut.Text, parts) }),
|
||||
widget.NewButton("Encrypt", func() { encAction(); updateMode() }),
|
||||
widget.NewButton("Copy", func() { copyClip(parts.cipherOut.Text, parts) }),
|
||||
widget.NewButton("Import Key QR", importPeerQR),
|
||||
)
|
||||
|
||||
group := container.NewVBox(
|
||||
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("Výsledek"), parts.cipherOut,
|
||||
container.NewHBox(widget.NewLabel("Výstup"), toggleBtn),
|
||||
outputContainer,
|
||||
)
|
||||
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") })
|
||||
}(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(
|
||||
widget.NewButton("Paste+Decrypt", func() {
|
||||
clip := fyne.CurrentApp().Clipboard().Content()
|
||||
parts.payload.SetText(clip)
|
||||
if parts.showPayloadQR {
|
||||
updatePayloadQR(clip)
|
||||
}
|
||||
decryptAction()
|
||||
}),
|
||||
// widget.NewButton("Decrypt", decryptAction),
|
||||
widget.NewButton("Clear", func() { parts.payload.SetText(""); parts.plainOut.SetText("") }),
|
||||
widget.NewButton("Clear", func() {
|
||||
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) }))
|
||||
group := container.NewVBox(
|
||||
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,
|
||||
)
|
||||
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/kr/text v0.2.0
|
||||
## 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
|
||||
## explicit
|
||||
github.com/nfnt/resize
|
||||
@ -158,6 +161,11 @@ github.com/rymdport/portal/notification
|
||||
github.com/rymdport/portal/openuri
|
||||
github.com/rymdport/portal/settings
|
||||
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
|
||||
## explicit; go 1.15
|
||||
github.com/spf13/cobra
|
||||
|
||||
Loading…
Reference in New Issue
Block a user