WIP essentials: QR
This commit is contained in:
14 files changed
+1010
No files matched your search
@@ -0,0 +1,8 @@
|
||||
package mujica
|
||||
|
||||
func init() {
|
||||
addMessages(map[string][2]string{
|
||||
"qr_code.label": {"QR code", "二维码"},
|
||||
"qr_code.too_long": {"Text too long for a QR code", "文字过长,无法生成二维码"},
|
||||
})
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package mujica
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"log/slog"
|
||||
"math"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// QRCodeOptions configure a QRCode.
|
||||
type QRCodeOptions struct {
|
||||
Level QRLevel
|
||||
// Size is the side in DIPs with the quiet zone; zero is 160.
|
||||
Size float32
|
||||
// Label names the code for assistive technology; empty uses "QR code".
|
||||
Label string
|
||||
}
|
||||
|
||||
// qrCodeCache keeps the last encoding so it runs only when the text or level changes.
|
||||
type qrCodeCache struct {
|
||||
text string
|
||||
level QRLevel
|
||||
m QRMatrix
|
||||
err error
|
||||
done bool
|
||||
}
|
||||
|
||||
// QRCode draws text as a QR code, encoded offline; too long a text shows an error.
|
||||
func QRCode(c *ui.Context, text string, opts QRCodeOptions) *ui.Element {
|
||||
_ = opts.Level.String()
|
||||
if !(opts.Size >= 0) || math.IsInf(float64(opts.Size), 1) {
|
||||
panic("mujica: QRCode Size must be finite and >= 0")
|
||||
}
|
||||
size := opts.Size
|
||||
if size == 0 {
|
||||
size = 160
|
||||
}
|
||||
k := Tokens(c)
|
||||
e := ui.Box(c).Size(size, size).Shrink(0).Role(ui.RoleImage).Label(cmp.Or(opts.Label, msg(c, "qr_code.label")))
|
||||
cache := ui.Local(e, "qr_code", func() qrCodeCache { return qrCodeCache{} })
|
||||
if !cache.done || cache.text != text || cache.level != opts.Level {
|
||||
m, err := EncodeQR([]byte(text), opts.Level)
|
||||
*cache = qrCodeCache{text: text, level: opts.Level, m: m, err: err, done: true}
|
||||
slog.Debug("mujica: qr_code encoded", "version", m.Version, "level", opts.Level, "err", err)
|
||||
}
|
||||
if cache.err != nil {
|
||||
e.Center().Padding(Density(c).Unit()*2).Background(k.Surface).Border(theme.BorderWidth, k.Danger).Radius(theme.CardRadius).
|
||||
Description(cache.err.Error())
|
||||
e.Children(func() {
|
||||
ui.Text(c, msg(c, "qr_code.too_long")).TextColor(k.Danger).FontSize(FontSize(c, theme.CaptionSize)).TextAlign(ui.Center)
|
||||
})
|
||||
return e
|
||||
}
|
||||
m := cache.m
|
||||
// Scanners need dark modules on a light field, in either theme.
|
||||
light, dark := theme.Light().Surface, theme.Light().Text
|
||||
e.Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
n := m.Size() + 8
|
||||
s := p.Scale()
|
||||
mod := float32(math.Floor(float64(r.W*s)/float64(n))) / s
|
||||
if mod <= 0 {
|
||||
return
|
||||
}
|
||||
ox := float32(math.Round(float64((r.X+(r.W-mod*float32(n))/2)*s))) / s
|
||||
oy := float32(math.Round(float64((r.Y+(r.H-mod*float32(n))/2)*s))) / s
|
||||
p.Fill(r, light, theme.ControlRadius)
|
||||
for y, row := range m.Modules {
|
||||
for x, on := range row {
|
||||
if on {
|
||||
p.Fill(ui.Rect{X: ox + float32(x+4)*mod, Y: oy + float32(y+4)*mod, W: mod, H: mod}, dark, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return e
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
package mujica
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// QRLevel is a QR code's error correction level. The zero value is QRMedium.
|
||||
type QRLevel int
|
||||
|
||||
const (
|
||||
QRMedium QRLevel = iota // M: about 15% recoverable
|
||||
QRLow // L: about 7%
|
||||
QRQuartile // Q: about 25%
|
||||
QRHigh // H: about 30%
|
||||
)
|
||||
|
||||
func (l QRLevel) String() string {
|
||||
switch l {
|
||||
case QRLow:
|
||||
return "L"
|
||||
case QRMedium:
|
||||
return "M"
|
||||
case QRQuartile:
|
||||
return "Q"
|
||||
case QRHigh:
|
||||
return "H"
|
||||
}
|
||||
panic(fmt.Sprintf("mujica: unknown QRLevel %d", int(l)))
|
||||
}
|
||||
|
||||
// index is the level's column in qrCodeTable.
|
||||
func (l QRLevel) index() int { return [4]int{1, 0, 2, 3}[l] }
|
||||
|
||||
// formatBits are the level's two format information bits.
|
||||
func (l QRLevel) formatBits() int { return [4]int{0, 1, 3, 2}[l] }
|
||||
|
||||
// qrCodeBlocks is one version and level: EC codewords per block, then two block groups.
|
||||
type qrCodeBlocks struct{ ec, n1, d1, n2, d2 int }
|
||||
|
||||
func (b qrCodeBlocks) data() int { return b.n1*b.d1 + b.n2*b.d2 }
|
||||
|
||||
// qrCodeTable is ISO/IEC 18004 Table 9, indexed [version-1][L, M, Q, H].
|
||||
var qrCodeTable = [40][4]qrCodeBlocks{
|
||||
{{7, 1, 19, 0, 0}, {10, 1, 16, 0, 0}, {13, 1, 13, 0, 0}, {17, 1, 9, 0, 0}},
|
||||
{{10, 1, 34, 0, 0}, {16, 1, 28, 0, 0}, {22, 1, 22, 0, 0}, {28, 1, 16, 0, 0}},
|
||||
{{15, 1, 55, 0, 0}, {26, 1, 44, 0, 0}, {18, 2, 17, 0, 0}, {22, 2, 13, 0, 0}},
|
||||
{{20, 1, 80, 0, 0}, {18, 2, 32, 0, 0}, {26, 2, 24, 0, 0}, {16, 4, 9, 0, 0}},
|
||||
{{26, 1, 108, 0, 0}, {24, 2, 43, 0, 0}, {18, 2, 15, 2, 16}, {22, 2, 11, 2, 12}},
|
||||
{{18, 2, 68, 0, 0}, {16, 4, 27, 0, 0}, {24, 4, 19, 0, 0}, {28, 4, 15, 0, 0}},
|
||||
{{20, 2, 78, 0, 0}, {18, 4, 31, 0, 0}, {18, 2, 14, 4, 15}, {26, 4, 13, 1, 14}},
|
||||
{{24, 2, 97, 0, 0}, {22, 2, 38, 2, 39}, {22, 4, 18, 2, 19}, {26, 4, 14, 2, 15}},
|
||||
{{30, 2, 116, 0, 0}, {22, 3, 36, 2, 37}, {20, 4, 16, 4, 17}, {24, 4, 12, 4, 13}},
|
||||
{{18, 2, 68, 2, 69}, {26, 4, 43, 1, 44}, {24, 6, 19, 2, 20}, {28, 6, 15, 2, 16}},
|
||||
{{20, 4, 81, 0, 0}, {30, 1, 50, 4, 51}, {28, 4, 22, 4, 23}, {24, 3, 12, 8, 13}},
|
||||
{{24, 2, 92, 2, 93}, {22, 6, 36, 2, 37}, {26, 4, 20, 6, 21}, {28, 7, 14, 4, 15}},
|
||||
{{26, 4, 107, 0, 0}, {22, 8, 37, 1, 38}, {24, 8, 20, 4, 21}, {22, 12, 11, 4, 12}},
|
||||
{{30, 3, 115, 1, 116}, {24, 4, 40, 5, 41}, {20, 11, 16, 5, 17}, {24, 11, 12, 5, 13}},
|
||||
{{22, 5, 87, 1, 88}, {24, 5, 41, 5, 42}, {30, 5, 24, 7, 25}, {24, 11, 12, 7, 13}},
|
||||
{{24, 5, 98, 1, 99}, {28, 7, 45, 3, 46}, {24, 15, 19, 2, 20}, {30, 3, 15, 13, 16}},
|
||||
{{28, 1, 107, 5, 108}, {28, 10, 46, 1, 47}, {28, 1, 22, 15, 23}, {28, 2, 14, 17, 15}},
|
||||
{{30, 5, 120, 1, 121}, {26, 9, 43, 4, 44}, {28, 17, 22, 1, 23}, {28, 2, 14, 19, 15}},
|
||||
{{28, 3, 113, 4, 114}, {26, 3, 44, 11, 45}, {26, 17, 21, 4, 22}, {26, 9, 13, 16, 14}},
|
||||
{{28, 3, 107, 5, 108}, {26, 3, 41, 13, 42}, {30, 15, 24, 5, 25}, {28, 15, 15, 10, 16}},
|
||||
{{28, 4, 116, 4, 117}, {26, 17, 42, 0, 0}, {28, 17, 22, 6, 23}, {30, 19, 16, 6, 17}},
|
||||
{{28, 2, 111, 7, 112}, {28, 17, 46, 0, 0}, {30, 7, 24, 16, 25}, {24, 34, 13, 0, 0}},
|
||||
{{30, 4, 121, 5, 122}, {28, 4, 47, 14, 48}, {30, 11, 24, 14, 25}, {30, 16, 15, 14, 16}},
|
||||
{{30, 6, 117, 4, 118}, {28, 6, 45, 14, 46}, {30, 11, 24, 16, 25}, {30, 30, 16, 2, 17}},
|
||||
{{26, 8, 106, 4, 107}, {28, 8, 47, 13, 48}, {30, 7, 24, 22, 25}, {30, 22, 15, 13, 16}},
|
||||
{{28, 10, 114, 2, 115}, {28, 19, 46, 4, 47}, {28, 28, 22, 6, 23}, {30, 33, 16, 4, 17}},
|
||||
{{30, 8, 122, 4, 123}, {28, 22, 45, 3, 46}, {30, 8, 23, 26, 24}, {30, 12, 15, 28, 16}},
|
||||
{{30, 3, 117, 10, 118}, {28, 3, 45, 23, 46}, {30, 4, 24, 31, 25}, {30, 11, 15, 31, 16}},
|
||||
{{30, 7, 116, 7, 117}, {28, 21, 45, 7, 46}, {30, 1, 23, 37, 24}, {30, 19, 15, 26, 16}},
|
||||
{{30, 5, 115, 10, 116}, {28, 19, 47, 10, 48}, {30, 15, 24, 25, 25}, {30, 23, 15, 25, 16}},
|
||||
{{30, 13, 115, 3, 116}, {28, 2, 46, 29, 47}, {30, 42, 24, 1, 25}, {30, 23, 15, 28, 16}},
|
||||
{{30, 17, 115, 0, 0}, {28, 10, 46, 23, 47}, {30, 10, 24, 35, 25}, {30, 19, 15, 35, 16}},
|
||||
{{30, 17, 115, 1, 116}, {28, 14, 46, 21, 47}, {30, 29, 24, 19, 25}, {30, 11, 15, 46, 16}},
|
||||
{{30, 13, 115, 6, 116}, {28, 14, 46, 23, 47}, {30, 44, 24, 7, 25}, {30, 59, 16, 1, 17}},
|
||||
{{30, 12, 121, 7, 122}, {28, 12, 47, 26, 48}, {30, 39, 24, 14, 25}, {30, 22, 15, 41, 16}},
|
||||
{{30, 6, 121, 14, 122}, {28, 6, 47, 34, 48}, {30, 46, 24, 10, 25}, {30, 2, 15, 64, 16}},
|
||||
{{30, 17, 122, 4, 123}, {28, 29, 46, 14, 47}, {30, 49, 24, 10, 25}, {30, 24, 15, 46, 16}},
|
||||
{{30, 4, 122, 18, 123}, {28, 13, 46, 32, 47}, {30, 48, 24, 14, 25}, {30, 42, 15, 32, 16}},
|
||||
{{30, 20, 117, 4, 118}, {28, 40, 47, 7, 48}, {30, 43, 24, 22, 25}, {30, 10, 15, 67, 16}},
|
||||
{{30, 19, 118, 6, 119}, {28, 18, 47, 31, 48}, {30, 34, 24, 34, 25}, {30, 20, 15, 61, 16}},
|
||||
}
|
||||
|
||||
// QRMatrix is an encoded QR code: Modules[y][x] is true for dark.
|
||||
type QRMatrix struct {
|
||||
Version int
|
||||
Level QRLevel
|
||||
Mask int
|
||||
Modules [][]bool
|
||||
}
|
||||
|
||||
// Size is the side of the symbol in modules, without the quiet zone.
|
||||
func (m QRMatrix) Size() int { return len(m.Modules) }
|
||||
|
||||
// EncodeQR encodes data in byte mode at the smallest version that fits, choosing the mask by penalty.
|
||||
func EncodeQR(data []byte, level QRLevel) (QRMatrix, error) {
|
||||
_ = level.String()
|
||||
for v := 1; v <= 40; v++ {
|
||||
cc := 8
|
||||
if v >= 10 {
|
||||
cc = 16
|
||||
}
|
||||
if len(data) >= 1<<cc {
|
||||
continue
|
||||
}
|
||||
if 4+cc+8*len(data) <= 8*qrCodeTable[v-1][level.index()].data() {
|
||||
return qrCodeEncodeAt(data, level, v), nil
|
||||
}
|
||||
}
|
||||
return QRMatrix{}, fmt.Errorf("mujica: %d bytes do not fit a QR code at level %v", len(data), level)
|
||||
}
|
||||
|
||||
// qrCodeEncodeAt builds version v with the lowest-penalty mask.
|
||||
func qrCodeEncodeAt(data []byte, level QRLevel, v int) QRMatrix {
|
||||
b := qrCodeTable[v-1][level.index()]
|
||||
var w qrCodeBits
|
||||
w.put(4, 4)
|
||||
if v >= 10 {
|
||||
w.put(len(data), 16)
|
||||
} else {
|
||||
w.put(len(data), 8)
|
||||
}
|
||||
for _, d := range data {
|
||||
w.put(int(d), 8)
|
||||
}
|
||||
capBits := 8 * b.data()
|
||||
w.put(0, min(4, capBits-w.n))
|
||||
w.put(0, (8-w.n%8)%8)
|
||||
for pad := 0xEC; w.n < capBits; pad ^= 0xEC ^ 0x11 {
|
||||
w.put(pad, 8)
|
||||
}
|
||||
words := qrCodeInterleave(w.bytes, b)
|
||||
g := newQRCodeGrid(v)
|
||||
g.place(words)
|
||||
best, bestPenalty := 0, -1
|
||||
for m := range 8 {
|
||||
t := g.clone()
|
||||
t.apply(m)
|
||||
if p := t.penalty(); bestPenalty < 0 || p < bestPenalty {
|
||||
best, bestPenalty = m, p
|
||||
}
|
||||
}
|
||||
g.apply(best)
|
||||
g.format(level.formatBits(), best)
|
||||
g.version(true)
|
||||
return QRMatrix{Version: v, Level: level, Mask: best, Modules: g.dark}
|
||||
}
|
||||
|
||||
// qrCodeBits appends bits most significant first.
|
||||
type qrCodeBits struct {
|
||||
bytes []byte
|
||||
n int
|
||||
}
|
||||
|
||||
func (w *qrCodeBits) put(v, count int) {
|
||||
for i := count - 1; i >= 0; i-- {
|
||||
if w.n%8 == 0 {
|
||||
w.bytes = append(w.bytes, 0)
|
||||
}
|
||||
if v>>i&1 == 1 {
|
||||
w.bytes[w.n/8] |= 0x80 >> (w.n % 8)
|
||||
}
|
||||
w.n++
|
||||
}
|
||||
}
|
||||
|
||||
// qrCodeInterleave splits data into blocks, adds Reed-Solomon codewords and interleaves both.
|
||||
func qrCodeInterleave(data []byte, b qrCodeBlocks) []byte {
|
||||
var blocks, ecs [][]byte
|
||||
gen := qrCodeGenerator(b.ec)
|
||||
at := 0
|
||||
for i := range b.n1 + b.n2 {
|
||||
n := b.d1
|
||||
if i >= b.n1 {
|
||||
n = b.d2
|
||||
}
|
||||
blk := data[at : at+n]
|
||||
at += n
|
||||
blocks = append(blocks, blk)
|
||||
ecs = append(ecs, qrCodeRemainder(blk, gen))
|
||||
}
|
||||
var out []byte
|
||||
for i := range max(b.d1, b.d2) {
|
||||
for _, blk := range blocks {
|
||||
if i < len(blk) {
|
||||
out = append(out, blk[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range b.ec {
|
||||
for _, e := range ecs {
|
||||
out = append(out, e[i])
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// qrCodeMul multiplies in GF(256) modulo x^8+x^4+x^3+x^2+1.
|
||||
func qrCodeMul(x, y byte) byte {
|
||||
var z byte
|
||||
for i := 7; i >= 0; i-- {
|
||||
hi := z & 0x80
|
||||
z <<= 1
|
||||
if hi != 0 {
|
||||
z ^= 0x1D
|
||||
}
|
||||
if y>>i&1 == 1 {
|
||||
z ^= x
|
||||
}
|
||||
}
|
||||
return z
|
||||
}
|
||||
|
||||
// qrCodeGenerator returns the degree-n generator's coefficients, highest first, leading 1 dropped.
|
||||
func qrCodeGenerator(n int) []byte {
|
||||
g := make([]byte, n)
|
||||
g[n-1] = 1
|
||||
root := byte(1)
|
||||
for range n {
|
||||
for j := range n {
|
||||
g[j] = qrCodeMul(g[j], root)
|
||||
if j+1 < n {
|
||||
g[j] ^= g[j+1]
|
||||
}
|
||||
}
|
||||
root = qrCodeMul(root, 2)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func qrCodeRemainder(data, gen []byte) []byte {
|
||||
r := make([]byte, len(gen))
|
||||
for _, d := range data {
|
||||
f := d ^ r[0]
|
||||
copy(r, r[1:])
|
||||
r[len(r)-1] = 0
|
||||
for i := range r {
|
||||
r[i] ^= qrCodeMul(gen[i], f)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// qrCodeAlignment returns the alignment pattern centers of version v.
|
||||
func qrCodeAlignment(v int) []int {
|
||||
if v == 1 {
|
||||
return nil
|
||||
}
|
||||
n := v/7 + 2
|
||||
step := 26
|
||||
if v != 32 {
|
||||
step = (v*4 + n*2 + 1) / (n*2 - 2) * 2
|
||||
}
|
||||
pos := make([]int, n)
|
||||
pos[0] = 6
|
||||
for i, p := n-1, v*4+10; i >= 1; i, p = i-1, p-step {
|
||||
pos[i] = p
|
||||
}
|
||||
return pos
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package mujica
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// testdata/qr: segno 1.6.6 byte-mode symbols, padding fixed to ISO 7.4.10.
|
||||
func TestEncodeQRMatchesReference(t *testing.T) {
|
||||
files, err := filepath.Glob("testdata/qr/*.txt")
|
||||
if err != nil || len(files) < 6 {
|
||||
t.Fatalf("reference files: %v %v", files, err)
|
||||
}
|
||||
levels := map[string]QRLevel{"L": QRLow, "M": QRMedium, "Q": QRQuartile, "H": QRHigh}
|
||||
for _, f := range files {
|
||||
b, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lines := strings.Split(strings.TrimSpace(string(b)), "\n")
|
||||
head := strings.Fields(lines[0])
|
||||
version, _ := strconv.Atoi(head[0])
|
||||
mask, _ := strconv.Atoi(head[2])
|
||||
data, err := hex.DecodeString(head[3])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m, err := EncodeQR(data, levels[head[1]])
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", f, err)
|
||||
}
|
||||
if m.Version != version || m.Mask != mask {
|
||||
t.Errorf("%s: version %d mask %d, want %d %d", f, m.Version, m.Mask, version, mask)
|
||||
continue
|
||||
}
|
||||
for y, row := range lines[1:] {
|
||||
for x, ch := range row {
|
||||
if m.Modules[y][x] != (ch == '1') {
|
||||
t.Fatalf("%s: module (%d,%d) differs", f, x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQRCodeAlignmentPositions(t *testing.T) {
|
||||
for v, want := range map[int]string{1: "[]", 2: "[6 18]", 7: "[6 22 38]", 32: "[6 34 60 86 112 138]", 40: "[6 30 58 86 114 142 170]"} {
|
||||
if got := fmtInts(qrCodeAlignment(v)); got != want {
|
||||
t.Errorf("version %d: %s, want %s", v, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fmtInts(v []int) string {
|
||||
s := make([]string, len(v))
|
||||
for i, n := range v {
|
||||
s[i] = strconv.Itoa(n)
|
||||
}
|
||||
return "[" + strings.Join(s, " ") + "]"
|
||||
}
|
||||
|
||||
func TestEncodeQRLimits(t *testing.T) {
|
||||
m, err := EncodeQR(make([]byte, 2953), QRLow)
|
||||
if err != nil || m.Version != 40 || m.Size() != 177 {
|
||||
t.Fatalf("largest L symbol: %v %v", m.Version, err)
|
||||
}
|
||||
if _, err := EncodeQR(make([]byte, 2954), QRLow); err == nil {
|
||||
t.Fatal("2954 bytes fit at L")
|
||||
}
|
||||
if _, err := EncodeQR(make([]byte, 1274), QRHigh); err == nil {
|
||||
t.Fatal("1274 bytes fit at H")
|
||||
}
|
||||
m, err = EncodeQR(nil, QRHigh)
|
||||
if err != nil || m.Version != 1 {
|
||||
t.Fatalf("empty data: %v %v", m.Version, err)
|
||||
}
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
package mujica
|
||||
|
||||
// qrCodeGrid is a symbol under construction; fixed marks function modules.
|
||||
type qrCodeGrid struct {
|
||||
v int
|
||||
dark, fixed [][]bool
|
||||
}
|
||||
|
||||
func newQRCodeGrid(v int) *qrCodeGrid {
|
||||
n := 4*v + 17
|
||||
g := &qrCodeGrid{v: v, dark: make([][]bool, n), fixed: make([][]bool, n)}
|
||||
for i := range n {
|
||||
g.dark[i], g.fixed[i] = make([]bool, n), make([]bool, n)
|
||||
}
|
||||
for i := range n {
|
||||
g.set(6, i, i%2 == 0)
|
||||
g.set(i, 6, i%2 == 0)
|
||||
}
|
||||
g.finder(3, 3)
|
||||
g.finder(n-4, 3)
|
||||
g.finder(3, n-4)
|
||||
al := qrCodeAlignment(v)
|
||||
for i, x := range al {
|
||||
for j, y := range al {
|
||||
if i == 0 && j == 0 || i == 0 && j == len(al)-1 || i == len(al)-1 && j == 0 {
|
||||
continue
|
||||
}
|
||||
for dy := -2; dy <= 2; dy++ {
|
||||
for dx := -2; dx <= 2; dx++ {
|
||||
g.set(x+dx, y+dy, max(qrCodeAbs(dx), qrCodeAbs(dy)) != 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reserve format and version areas; apply fills them.
|
||||
g.format(-1, 0)
|
||||
g.version(false)
|
||||
return g
|
||||
}
|
||||
|
||||
func qrCodeAbs(v int) int { return max(v, -v) }
|
||||
|
||||
func (g *qrCodeGrid) set(x, y int, dark bool) {
|
||||
g.dark[y][x], g.fixed[y][x] = dark, true
|
||||
}
|
||||
|
||||
// finder draws a finder pattern and its separator centered at (cx, cy).
|
||||
func (g *qrCodeGrid) finder(cx, cy int) {
|
||||
n := len(g.dark)
|
||||
for dy := -4; dy <= 4; dy++ {
|
||||
for dx := -4; dx <= 4; dx++ {
|
||||
x, y := cx+dx, cy+dy
|
||||
if x < 0 || y < 0 || x >= n || y >= n {
|
||||
continue
|
||||
}
|
||||
d := max(qrCodeAbs(dx), qrCodeAbs(dy))
|
||||
g.set(x, y, d != 2 && d != 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// format writes the 15 format bits and the dark module; levelBits -1 leaves them light.
|
||||
func (g *qrCodeGrid) format(levelBits, mask int) {
|
||||
data := levelBits<<3 | mask
|
||||
rem := data
|
||||
for range 10 {
|
||||
rem = rem<<1 ^ (rem>>9)*0x537
|
||||
}
|
||||
b := (data<<10 | rem) ^ 0x5412
|
||||
bit := func(i int) bool { return levelBits >= 0 && b>>i&1 == 1 }
|
||||
n := len(g.dark)
|
||||
for i := 0; i <= 5; i++ {
|
||||
g.set(8, i, bit(i))
|
||||
}
|
||||
g.set(8, 7, bit(6))
|
||||
g.set(8, 8, bit(7))
|
||||
g.set(7, 8, bit(8))
|
||||
for i := 9; i < 15; i++ {
|
||||
g.set(14-i, 8, bit(i))
|
||||
}
|
||||
for i := range 8 {
|
||||
g.set(n-1-i, 8, bit(i))
|
||||
}
|
||||
for i := 8; i < 15; i++ {
|
||||
g.set(8, n-15+i, bit(i))
|
||||
}
|
||||
g.set(8, n-8, levelBits >= 0)
|
||||
}
|
||||
|
||||
// version writes the 18 version bits from version 7 on, or light ones.
|
||||
func (g *qrCodeGrid) version(on bool) {
|
||||
if g.v < 7 {
|
||||
return
|
||||
}
|
||||
rem := g.v
|
||||
for range 12 {
|
||||
rem = rem<<1 ^ (rem>>11)*0x1F25
|
||||
}
|
||||
b := g.v<<12 | rem
|
||||
n := len(g.dark)
|
||||
for i := range 18 {
|
||||
dark := on && b>>i&1 == 1
|
||||
a, c := n-11+i%3, i/3
|
||||
g.set(a, c, dark)
|
||||
g.set(c, a, dark)
|
||||
}
|
||||
}
|
||||
|
||||
// place lays the codewords in the zigzag from the bottom right; remainder bits stay light.
|
||||
func (g *qrCodeGrid) place(words []byte) {
|
||||
n := len(g.dark)
|
||||
i := 0
|
||||
for right := n - 1; right >= 1; right -= 2 {
|
||||
if right == 6 {
|
||||
right = 5
|
||||
}
|
||||
for vert := range n {
|
||||
for j := range 2 {
|
||||
x := right - j
|
||||
up := (right+1)&2 == 0
|
||||
y := vert
|
||||
if up {
|
||||
y = n - 1 - vert
|
||||
}
|
||||
if g.fixed[y][x] || i >= len(words)*8 {
|
||||
continue
|
||||
}
|
||||
g.dark[y][x] = words[i/8]>>(7-i%8)&1 == 1
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *qrCodeGrid) clone() *qrCodeGrid {
|
||||
t := &qrCodeGrid{v: g.v, dark: make([][]bool, len(g.dark)), fixed: g.fixed}
|
||||
for i, row := range g.dark {
|
||||
t.dark[i] = append([]bool(nil), row...)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// apply XORs mask over the data modules; the information areas stay light, as 7.8.3 evaluates them.
|
||||
func (g *qrCodeGrid) apply(mask int) {
|
||||
for y, row := range g.dark {
|
||||
for x := range row {
|
||||
if g.fixed[y][x] {
|
||||
continue
|
||||
}
|
||||
var flip bool
|
||||
switch mask {
|
||||
case 0:
|
||||
flip = (x+y)%2 == 0
|
||||
case 1:
|
||||
flip = y%2 == 0
|
||||
case 2:
|
||||
flip = x%3 == 0
|
||||
case 3:
|
||||
flip = (x+y)%3 == 0
|
||||
case 4:
|
||||
flip = (x/3+y/2)%2 == 0
|
||||
case 5:
|
||||
flip = x*y%2+x*y%3 == 0
|
||||
case 6:
|
||||
flip = (x*y%2+x*y%3)%2 == 0
|
||||
case 7:
|
||||
flip = ((x+y)%2+x*y%3)%2 == 0
|
||||
}
|
||||
row[x] = row[x] != flip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// penalty scores the symbol by the four rules of ISO/IEC 18004 7.8.3.
|
||||
func (g *qrCodeGrid) penalty() int {
|
||||
n := len(g.dark)
|
||||
p, dark := 0, 0
|
||||
row, col := make([]bool, n), make([]bool, n)
|
||||
for i := range n {
|
||||
for j := range n {
|
||||
row[j], col[j] = g.dark[i][j], g.dark[j][i]
|
||||
if row[j] {
|
||||
dark++
|
||||
}
|
||||
if i > 0 && j > 0 && row[j] == row[j-1] && row[j] == g.dark[i-1][j] && row[j] == g.dark[i-1][j-1] {
|
||||
p += 3
|
||||
}
|
||||
}
|
||||
p += qrCodeRuns(row) + qrCodeFinders(row) + qrCodeRuns(col) + qrCodeFinders(col)
|
||||
}
|
||||
return p + 10*(qrCodeAbs(dark*100-50*n*n)/(5*n*n))
|
||||
}
|
||||
|
||||
// qrCodeRuns scores runs of five or more equal modules.
|
||||
func qrCodeRuns(seq []bool) int {
|
||||
p, run := 0, 1
|
||||
for j := 1; j <= len(seq); j++ {
|
||||
if j < len(seq) && seq[j] == seq[j-1] {
|
||||
run++
|
||||
continue
|
||||
}
|
||||
if run >= 5 {
|
||||
p += run - 2
|
||||
}
|
||||
run = 1
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// qrCodeFinders scores 1:1:3:1:1 patterns with four light modules on a side.
|
||||
func qrCodeFinders(seq []bool) int {
|
||||
core := [7]bool{true, false, true, true, true, false, true}
|
||||
n, p := len(seq), 0
|
||||
light := func(a, b int) bool {
|
||||
for i := max(a, 0); i < min(b, n); i++ {
|
||||
if seq[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
for idx := 0; idx+7 <= n; {
|
||||
match := true
|
||||
for i, d := range core {
|
||||
if seq[idx+i] != d {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
idx++
|
||||
continue
|
||||
}
|
||||
if idx == 0 || idx == n-7 || light(idx-4, idx) || light(idx+7, idx+11) {
|
||||
p += 40
|
||||
idx += 7
|
||||
} else {
|
||||
idx += 4
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package mujica
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
func TestQRCodeDrawsModulesAndCaches(t *testing.T) {
|
||||
text := "HELLO"
|
||||
var box ui.Rect
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
Use(c, Settings{Mode: Dark})
|
||||
ui.Row(c).Children(func() { box = QRCode(c, text, QRCodeOptions{Level: QRLow, Size: 116}).Bounds() })
|
||||
}, 200, 200)
|
||||
tt.Frame()
|
||||
if box.W != 116 {
|
||||
t.Fatalf("size %v", box)
|
||||
}
|
||||
m, _ := EncodeQR([]byte(text), QRLow)
|
||||
// 21 modules plus 8 of quiet zone in 116 DIPs: 4 DIPs each.
|
||||
px := func(x, y int) ui.Color {
|
||||
r, g, b, _ := tt.Image().At(int(box.X)+(x+4)*4+2, int(box.Y)+(y+4)*4+2).RGBA()
|
||||
return ui.RGB(uint8(r>>8), uint8(g>>8), uint8(b>>8))
|
||||
}
|
||||
for _, at := range [][2]int{{0, 0}, {3, 3}, {7, 7}, {10, 6}, {12, 12}} {
|
||||
want := theme.Light().Surface
|
||||
if m.Modules[at[1]][at[0]] {
|
||||
want = theme.Light().Text
|
||||
}
|
||||
if got := px(at[0], at[1]); got != want {
|
||||
t.Errorf("module %v is %v, want %v", at, got, want)
|
||||
}
|
||||
}
|
||||
if _, ok := tt.Find("QR code"); !ok {
|
||||
t.Fatal("no accessible name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQRCodeTooLong(t *testing.T) {
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
Use(c, Settings{})
|
||||
QRCode(c, strings.Repeat("x", 1300), QRCodeOptions{Level: QRHigh})
|
||||
}, 300, 300)
|
||||
if !tt.HasText("Text too long for a QR code") {
|
||||
t.Fatalf("texts %q", tt.Texts())
|
||||
}
|
||||
}
|
||||
|
||||
func TestQRCodeRejectsBadOptions(t *testing.T) {
|
||||
for name, o := range map[string]QRCodeOptions{"level": {Level: 9}, "size": {Size: -1}} {
|
||||
func() {
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
t.Errorf("%s: no panic", name)
|
||||
}
|
||||
}()
|
||||
ui.NewTester(func(c *ui.Context) { Use(c, Settings{}); QRCode(c, "x", o) }, 200, 200)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func TestQRCodeSnapshot(t *testing.T) {
|
||||
snapshot(t, "qr_code", 420, 200, func(c *ui.Context) {
|
||||
ui.Row(c).Padding(16).Gap(16).Children(func() {
|
||||
QRCode(c, "https://example.com/mujica", QRCodeOptions{})
|
||||
QRCode(c, "MujicaUI", QRCodeOptions{Level: QRHigh, Size: 100})
|
||||
QRCode(c, strings.Repeat("x", 1300), QRCodeOptions{Level: QRHigh, Size: 100})
|
||||
})
|
||||
})
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
1 L 4 48454c4c4f
|
||||
111111101111101111111
|
||||
100000101010101000001
|
||||
101110101000001011101
|
||||
101110101111001011101
|
||||
101110100000101011101
|
||||
100000101101001000001
|
||||
111111101010101111111
|
||||
000000000111100000000
|
||||
110011100101000101111
|
||||
101001000101100101111
|
||||
000101101111001110010
|
||||
100000011000011010000
|
||||
111011110100111000110
|
||||
000000001000111001011
|
||||
111111100100110001010
|
||||
100000101101100100010
|
||||
101110101011001110101
|
||||
101110100111100001011
|
||||
101110100011001111000
|
||||
100000101110011000000
|
||||
111111101110111110101
|
||||
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
18 H 7 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff4d756a69636155494d756a69636155494d756a69636155494d756a69636155494d756a69636155494d756a6963615549
|
||||
11111110100011001001111011010000010111101000100000010111000011001100110010001011101111111
|
||||
10000010101011101101011100110101101000101010010110111010110010101001011001101101001000001
|
||||
10111010001001001111010000100101011110100001011011110100001001100001010111110100001011101
|
||||
10111010100100010101001110100110011011010101000010101110111111000100010001010110101011101
|
||||
10111010101010111100111110011111101001100110111101011011111000110110110110110101001011101
|
||||
10000010100000010011011110101000101011101010001101001110001110001010000111101101001000001
|
||||
11111110101010101010101010101010101010101010101010101010101010101010101010101010101111111
|
||||
00000000000100111011011011011000110111111010011111101010001010001100011010010101100000000
|
||||
00010010010011001101111101111111111101110010000000011011111001110001110101100000100111011
|
||||
10101101110010101001001010110100000011101100111000001100111100100010001000100111101011101
|
||||
11001010010011001011011100010001110110101010100100000101101110010000110111110101111010110
|
||||
11110000111100111011111111011011111000000010010001001001010111000011111111011010100110100
|
||||
00011111010010111001101001111011001001101010011011011010101110011101110011001100010010110
|
||||
01010100101111110011101011110110000011001001011100111101010000110000101111010000010100010
|
||||
01100111001111011000101010110001101010011010000001010011101000110001100001010111010001011
|
||||
00110100101010000011010010100001100101100111010011111011111111010100110111000100100111000
|
||||
11011011110100011110000001001001101110100100010111110100001101111001001001001000111110010
|
||||
00001100000011111111011101111010100110111111011001010010111101011111001010011111000000001
|
||||
10100111011010111101110011011000101010111111110010101010001110011101100111111011110000110
|
||||
10100100110000110010110111110010110101000001001111000001111110111111110110101111010110010
|
||||
10111010001101001111110100110101001000001010110010111101110011011010000110101101010100000
|
||||
11110101111110111001001000100111101000100001111110010100100111101110100010001001010101111
|
||||
10111011110011100011110101100100000100100101001011011011100100010100111111010111011111011
|
||||
00011101001011101011111010111010110101001001011011100101101101110100010011100000001011101
|
||||
01001111001100011011100000101110000000111101111011010101100100110010011001100110100101100
|
||||
11111100110100101110101001000110011010110001000101011011001110100111100011100010101100101
|
||||
10111010100111110001100011100100001100111000010000110111010010010001110000110010111111010
|
||||
10111000100010011100000000100111110110011010000110101101111000101001101001110110111111010
|
||||
10101111110101100000000101001111111100101100111100010111111110100111110010101111111111011
|
||||
10101000111001101001101111101000110111110111001000100010001111110001110000110101100011010
|
||||
11111010101011010010001001011010101111101110010101010110101110101100110011001000101010110
|
||||
11001000110100110111101011001000110000111000001000111010001001000100111000011101100010001
|
||||
00111111101010100101011100011111111110110101110010110011111000000110100100000111111111110
|
||||
01110100010100001010101100111001010010001000010110100110000011010111011100100010111100001
|
||||
11110110001111001101001101001010010111001000110111010011010101101011011000000001101110001
|
||||
00000001010110101011111100100111010111011000110011011110100001011110100000001011100110111
|
||||
10001111111111010000000001001000010010010011000100101001010111001010100000000011100110110
|
||||
01110101010101111111001101111110011111001011110010010100010100001101010000101000001001000
|
||||
01110110111011101011001011010001010010000111111010100011101111001110010111100000111010100
|
||||
01111000101111011100100000111101100111010010100001111100011101011100001100111000000110001
|
||||
11010110010110100001001100010011001010110011111110101101111101001101000001101101001110111
|
||||
01000100000000100000011110001100101110100110000000011000001001000000111011010111010010111
|
||||
01110010001101101110101110100100100011000011100001110101011001100010001010001100001111100
|
||||
00100001110101101010001100100110001001111010010000111100100010101110011110010011011001011
|
||||
01101011010011011011010111100011000001100010011100011001010110101100110100100001001010101
|
||||
11111001101110000110011010000001000010100111010011110011111001011101110111111001101010000
|
||||
00010011101111111111011101111010101100100011100000001110100011000111100011000101001111101
|
||||
11101000011100000110100100101011111100001100000111000110111111100001110011111010110000100
|
||||
11111010100111100010000011110011101010001010011001000000110001100110000000100011001111001
|
||||
00100000100100111111110000110100011101001000111111100010010000110111111100100110000100011
|
||||
01001011001011100101100110001011100000010100000000110111011100100010001000110101101101100
|
||||
01111001101101011101001100001111010101100101010000110010011111110111111011101110110011111
|
||||
00010011000010000100010100100110011001000101011101001010101010101110110001010110110011100
|
||||
00101100110100111101000011010110110011100000110100110010001011101000010011110010011101101
|
||||
01111111110111000100011111101111100010101010010000110111111011001001111110101100111110011
|
||||
11111000101000010111101000001000110010001111100011000010001110011101111010000001100010100
|
||||
01111010110010101100110011001010100110001001001010001110101100000110101101010111101011001
|
||||
10111000111100000111101000001000110101000111101110000110001111110111111111010110100011011
|
||||
00001111101000100111001010111111101101111100011011010111111000010001110001101101111110110
|
||||
10111100001110011110110000000110001111001011001000011011001101011010011111011000001011010
|
||||
10001010001110011001101000000010001011111111011100000011001010111010101110111111101000011
|
||||
00001100001000010000010101111001111110111011001101111100111011000101011000001101110100001
|
||||
01001010111011000100111111011101011101101011010010101100011110011001000111001111100100011
|
||||
01100101010011110111101001010100001111110111011001100000100000001000100010011100111111100
|
||||
11001010010110110101001000111110001111000011101001100010010000001101011100011010000110010
|
||||
11111101110001011011011010100011010100111111100001101110000110010011111101000001001000010
|
||||
01101110111110000010110111001000101010000010011111101000111010111101000001100011100101011
|
||||
10101101100011110111111001100111111111011101100110101000011111100011100111101010001111110
|
||||
00101110100111110111010110110100110111011001000101101011011011100001010011111000011110011
|
||||
11110101001100110111010001110100000111001001000001000111111010000101110111011101110100101
|
||||
10111111000011010010111111011010101101110111010010011111010101101101101010100010001111000
|
||||
00100100001100000000110101000001000101110100100010100001000010011110001010000111110110001
|
||||
10000011001111010110111100101101011110001000001101110001110000000001000100010000011010111
|
||||
01111000111000001101011100100000001001001010100100101010111101111010110110110111001101110
|
||||
01110110010010000011101111101110110110011000110001011011011111000110110100100011011111001
|
||||
11111000001000010011001111011111010010110010101101101111111110001001100000010000001100110
|
||||
11101110100111111001110001010110111000011000100001100110001001010000101111010001110100101
|
||||
01100100001010000001110010110010001101100111000000011010100010101100010111101000111100000
|
||||
10010010110001100010101010000100111011100101110010110111001011101010110010001111011110101
|
||||
11001101101000010100111000110111011001110010100001011110011111100010100001111011010011101
|
||||
10010010110011101100111101111111100101001010101100000111111101010011111001010010111110100
|
||||
00000000100111100111001010011000111111010100011101010010001100101010101011101110100011110
|
||||
11111110010000000110100100001010110101000111000011110010101101001111000000001001101010000
|
||||
10000010001100100100001000101000101111110111101100011110001110111100100000101101100010001
|
||||
10111010010110011100100111011111111101010101001011011111111111111111111111101111111111000
|
||||
10111010100111001001001111001011011111111000010101000010010101011000001100011100001110111
|
||||
10111010011110001100111110000001010110010101100011110110001010111011001011111101010010101
|
||||
10000010010100100000000110110110011001110010010011111000011010111010001100101011000000010
|
||||
11111110010101101000100101001010110001111100100010001101100101111010000101101011010111010
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
8 Q 7 436f75727420676f7468696320636f6d706f6e656e747320666f72204d79476f2e20436f75727420676f7468696320636f6d706f6e656e747320666f72204d79476f2e20436f75727420676f7468696320636f6d706f6e656e747320666f72204d79476f2e20
|
||||
1111111010000000011000011010001101101100101111111
|
||||
1000001001110010010000011101111111110111101000001
|
||||
1011101011011000111001101101000010110001101011101
|
||||
1011101010000101101110110110100000100001001011101
|
||||
1011101001110100001101111110111000001000001011101
|
||||
1000001010110100011000100010111111101010001000001
|
||||
1111111010101010101010101010101010101010101111111
|
||||
0000000010011100110000100010110101011110000000000
|
||||
0101011111100110100111111110111001111110011101101
|
||||
1110000001011111100110101101000000001000010011011
|
||||
0010101010110111110001110101001110010000000110111
|
||||
1111100010101110100010010101011101001101010100010
|
||||
0100011010111010111010100000010111011010010011011
|
||||
0011110101011111010100001100000000110100100001101
|
||||
1001101100011110010011010110110000011010110101001
|
||||
0011110000100101001011000100101110101011001011001
|
||||
1000111010110011111011101000100110111010010010110
|
||||
1100100110001000010010011000111101010010010101001
|
||||
1010101010000100010001101010101100010100110101011
|
||||
1001000100000000010001111110101011011101110110101
|
||||
1001101000011010111000111011100101101110010100000
|
||||
1000110000100011001110111101000010000000000000110
|
||||
1000111111110000110000111110000000100100111110111
|
||||
0001100010111001101001100010011100101011100010001
|
||||
0100101011100111010110101010100111101001101010011
|
||||
1100100010011010100001100010110110111101100010101
|
||||
1011111111111000100110111110110101011010111111101
|
||||
0000000000101101101110111010110111011000101000010
|
||||
0110001000011100110101101111001010011110011101111
|
||||
1011110100110010010001101101110110000010100010111
|
||||
1100101000111000111101011101000010011000111000011
|
||||
1111010100011101111100110011110000111000001100100
|
||||
0001001010010000010001100101000111001010011011001
|
||||
1100010101101101011000100111100110001100110000011
|
||||
0100011111111010101101011101100000111100011110011
|
||||
1000100110011000000010111001111000101100001111001
|
||||
0001111011111011100011110001100111001110001100010
|
||||
0110100001010111011011010110100100101100110101111
|
||||
0100011010111000011100101111110110011010101000101
|
||||
0111000110000100010100010100110111010110011000001
|
||||
1110001100001010111101111111011110011010111110101
|
||||
0000000011011100100111100010100001000011100010101
|
||||
1111111011100101010110101011011100101010101011011
|
||||
1000001011111010001011100011001011111110100010110
|
||||
1011101000010000100010111111101101001110111111011
|
||||
1011101011100000000001100000100011010001101010011
|
||||
1011101000001111011101101011110110100000010011011
|
||||
1000001010111010110010001001000111111001110100000
|
||||
1111111001000101010110100001101111111100110010011
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
2 M 1 68747470733a2f2f6578616d706c652e636f6d2f6d756a696361
|
||||
1111111011100010001111111
|
||||
1000001000110110101000001
|
||||
1011101010101100101011101
|
||||
1011101001100110001011101
|
||||
1011101000011010001011101
|
||||
1000001011100010001000001
|
||||
1111111010101010101111111
|
||||
0000000001110011100000000
|
||||
1010001100110010100100101
|
||||
1110110100110101011101011
|
||||
0100101101010001010011101
|
||||
1110100110110010100101000
|
||||
0001101111101001101100001
|
||||
0011110111000101101100011
|
||||
1110101011010101111001101
|
||||
0011010110000010110111000
|
||||
1100001100111000111110010
|
||||
0000000010111010100010001
|
||||
1111111011010100101010001
|
||||
1000001000110011100010001
|
||||
1011101000111001111110010
|
||||
1011101001011100010010110
|
||||
1011101011010011110111011
|
||||
1000001001100001111110000
|
||||
1111111011101000101001001
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
8 L 2 78787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878
|
||||
1111111001110110010100001101011000001000101111111
|
||||
1000001010011111010000100111110010100011101000001
|
||||
1011101000010011100011001010100111110001101011101
|
||||
1011101011110110010100010000001101011101001011101
|
||||
1011101000011001001001111111011000001100001011101
|
||||
1000001011100010000101100011110010100110001000001
|
||||
1111111010101010101010101010101010101010101111111
|
||||
0000000000010001001001100010100111110011100000000
|
||||
1111101110010100111110111110001101011000110101010
|
||||
1110100111010110010101100101011000001100011001110
|
||||
1001111000111001001001010111110010100111010011011
|
||||
0001010000110101111010010010100111110011100110001
|
||||
0010111111110001001000111000001101011000101100100
|
||||
0101100101101100111110101101011000001100011001110
|
||||
1111101000100011100011100111110010100111010011011
|
||||
0010010010001000101110101010100111110011100110001
|
||||
1010101000110100111111001000001101011000101100100
|
||||
1101000111111110010101101101011000001100011001110
|
||||
0011001000000111010001000111110010100111010011011
|
||||
0101110000001011100010010010100111110011100110001
|
||||
0011011000100110010100111000001101011000101100100
|
||||
1100010101001001001001001101011000001100011001110
|
||||
1111111110111010000101111111110010100111111111011
|
||||
0001100011111001001000100010100111110011100010001
|
||||
0101101010100100111111101010001101011001101010100
|
||||
1011100010100110010101100011011000001100100011110
|
||||
0001111111100001001001111111110010100110111111011
|
||||
0100000011111101111010100110100111110010110010001
|
||||
1011001101110001001001010010001101011001000110100
|
||||
1000010010110100111111011001011000001101001101110
|
||||
1111111010101011100010101101110010100110111001011
|
||||
1000110011000000101110100110100111110010110010001
|
||||
1010101100010100111110110010001101011001000110100
|
||||
0111100001011110010100011001011000001101001101110
|
||||
1111101111101111010001101101110010100110111001011
|
||||
1000000111101011100010000110100111110010110010001
|
||||
1100001010111110010101010010001101011001000110100
|
||||
1110100110001001001001011001011000001101001101110
|
||||
0100011101111010000100101101110010100110111001011
|
||||
0111000000101001001001100110100111110010110010001
|
||||
1110001011110100111111111110001101011001111110101
|
||||
0000000010011110010101100011011000001101100011110
|
||||
1111111011101001001000101011110010100111101011011
|
||||
1000001000100101111011100010100111110011100010001
|
||||
1011101011101001001001111110001101011000111110100
|
||||
1011101010110100111110010011011000001100100111100
|
||||
1011101011100011100011111001110010100110001101000
|
||||
1000001011110000101111001100100111110011011000001
|
||||
1111111010110100111110000110001101011001110010111
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
2 M 2 e5aeabe5bbb7e593a5e789b9e7bb84e4bbb6e5ba93
|
||||
1111111001000010001111111
|
||||
1000001000001110101000001
|
||||
1011101010111100001011101
|
||||
1011101010010110101011101
|
||||
1011101011011000001011101
|
||||
1000001010100010001000001
|
||||
1111111010101010101111111
|
||||
0000000011111111100000000
|
||||
1011111000001101001111100
|
||||
0010110001110110100111100
|
||||
0001011110011100011100100
|
||||
0101010111001101110100100
|
||||
0000101110001001000011110
|
||||
1000010101000011001010000
|
||||
1011011110111100100110000
|
||||
1001110100110010011001111
|
||||
1010001111010000111110111
|
||||
0000000011001100100011100
|
||||
1111111000100110101010110
|
||||
1000001011101101100011111
|
||||
1011101011001010111110111
|
||||
1011101010000000100100011
|
||||
1011101010111000010010101
|
||||
1000001001110011100100101
|
||||
1111111011010000111101111
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
Reference in new issue
Block a user