├── bin ├── linux_amd64 │ └── goqr └── darwin_amd64 │ └── goqr ├── .gitignore ├── README.md ├── pkg ├── qr.go ├── gf256 │ └── gf256.go ├── png.go └── coding │ └── qr.go └── main.go /bin/linux_amd64/goqr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaymondChou/goqr/HEAD/bin/linux_amd64/goqr -------------------------------------------------------------------------------- /bin/darwin_amd64/goqr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaymondChou/goqr/HEAD/bin/darwin_amd64/goqr -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Numerous always-ignore extensions 2 | *.diff 3 | *.err 4 | *.orig 5 | *.log 6 | *.rej 7 | *.swo 8 | *.swp 9 | *.vi 10 | *~ 11 | *.sass-cache 12 | 13 | # OS or Editor folders 14 | .DS_Store 15 | Thumbs.db 16 | .cache 17 | .project 18 | .settings 19 | .tmproj 20 | *.esproj 21 | nbproject 22 | 23 | # Dreamweaver added files 24 | _notes 25 | dwsync.xml 26 | 27 | # Komodo 28 | *.komodoproject 29 | .komodotools 30 | 31 | # Folders to ignore 32 | .hg 33 | .svn 34 | .CVS 35 | intermediate 36 | publish 37 | .idea 38 | *.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | goqr 2 | ==== 3 | 4 | A fast qrcode generate write with google golang. 5 | 基于Go语言的快速二维码批量生成器 6 | 7 | //English is bad , more english readme will come soon. 8 | 9 | ###安装测试 10 | 11 | 1.安装Go语言编译环境 12 | 13 | 2.安装扩展包 14 | 15 | go get github.com/freezestart/goqr/pkg 16 | 17 | 3.编译 18 | 19 | go build main.go 20 | 21 | 4.批量生成 22 | 23 | goqr -data=sometext,anothertext,moretext 24 | 25 | png图片将按照输入顺序按序号生成在main目录下的output目录 26 | 27 | 5.服务端接口 28 | 29 | goqr -server 30 | 31 | 6.指定服务启动端口 32 | 33 | goqr -server -port=8888 34 | 35 | 默认8889 36 | 37 | ###测试结果 38 | 39 | 测试环境 MacBook Pro / i5 2.5G / 4G / Go 1.0.1 40 | 41 | 生成并输出1000个二维码 耗时8秒 平均每个耗时8毫秒 42 | -------------------------------------------------------------------------------- /pkg/qr.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package qr encodes QR codes. 3 | */ 4 | package qr 5 | 6 | import ( 7 | "errors" 8 | "github.com/RaymondChou/goqr/pkg/coding" 9 | "image" 10 | "image/color" 11 | ) 12 | 13 | // A Level denotes a QR error correction level. 14 | // From least to most tolerant of errors, they are L, M, Q, H. 15 | type Level int 16 | 17 | const ( 18 | L Level = iota // 20% redundant 19 | M // 38% redundant 20 | Q // 55% redundant 21 | H // 65% redundant 22 | ) 23 | 24 | // Encode returns an encoding of text at the given error correction level. 25 | func Encode(text string, level Level) (*Code, error) { 26 | // Pick data encoding, smallest first. 27 | // We could split the string and use different encodings 28 | // but that seems like overkill for now. 29 | var enc coding.Encoding 30 | switch { 31 | case coding.Num(text).Check() == nil: 32 | enc = coding.Num(text) 33 | case coding.Alpha(text).Check() == nil: 34 | enc = coding.Alpha(text) 35 | default: 36 | enc = coding.String(text) 37 | } 38 | 39 | // Pick size. 40 | l := coding.Level(level) 41 | var v coding.Version 42 | for v = coding.MinVersion; ; v++ { 43 | if v > coding.MaxVersion { 44 | return nil, errors.New("text too long to encode as QR") 45 | } 46 | if enc.Bits(v) <= v.DataBytes(l)*8 { 47 | break 48 | } 49 | } 50 | 51 | // Build and execute plan. 52 | p, err := coding.NewPlan(v, l, 0) 53 | if err != nil { 54 | return nil, err 55 | } 56 | cc, err := p.Encode(enc) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | // TODO: Pick appropriate mask. 62 | 63 | return &Code{cc.Bitmap, cc.Size, cc.Stride, 8}, nil 64 | } 65 | 66 | // A Code is a square pixel grid. 67 | // It implements image.Image and direct PNG encoding. 68 | type Code struct { 69 | Bitmap []byte // 1 is black, 0 is white 70 | Size int // number of pixels on a side 71 | Stride int // number of bytes per row 72 | Scale int // number of image pixels per QR pixel 73 | } 74 | 75 | // Black returns true if the pixel at (x,y) is black. 76 | func (c *Code) Black(x, y int) bool { 77 | return 0 <= x && x < c.Size && 0 <= y && y < c.Size && 78 | c.Bitmap[y*c.Stride+x/8]&(1<>>>>> " + qrdata) 61 | 62 | go output(qrdata, i, true) 63 | 64 | <-ch 65 | 66 | } 67 | 68 | endtime := time.Now().Unix() 69 | 70 | fmt.Println("completed time in seconds : " + fmt.Sprintf("%d", endtime-begintime)) 71 | 72 | } 73 | 74 | } 75 | 76 | func output(data string, i int, goroutine bool) { 77 | 78 | c, err := qr.Encode(data, qr.L) 79 | 80 | if err != nil { 81 | fmt.Println(err) 82 | } 83 | 84 | pngdat := c.PNG() 85 | 86 | if true { 87 | ioutil.WriteFile("output/"+fmt.Sprint(i+1)+".png", pngdat, 0666) 88 | } 89 | 90 | // m, err := png.Decode(bytes.NewBuffer(pngdat)) 91 | 92 | // if err != nil { 93 | // fmt.Println(err) 94 | // } 95 | 96 | // gm := m.(*image.Gray) 97 | 98 | // scale := c.Scale 99 | // siz := c.Size 100 | // nbad := 0 101 | 102 | // for y := 0; y < scale*(8+siz); y++ { 103 | 104 | // for x := 0; x < scale*(8+siz); x++ { 105 | 106 | // v := byte(255) 107 | 108 | // if c.Black(x/scale-4, y/scale-4) { 109 | // v = 0 110 | // } 111 | 112 | // if gv := gm.At(x, y).(color.Gray).Y; gv != v { 113 | // fmt.Println("%d,%d = %d, want %d", x, y, gv, v) 114 | // if nbad++; nbad >= 20 { 115 | // fmt.Println("too many bad pixels") 116 | // } 117 | // } 118 | 119 | // } 120 | 121 | // } 122 | if goroutine == true { 123 | ch <- 1 124 | } 125 | } 126 | 127 | func api(w http.ResponseWriter, r *http.Request) { 128 | 129 | var vaild bool 130 | 131 | r.ParseForm() 132 | 133 | for k, v := range r.Form { 134 | 135 | if k == "data" { 136 | fmt.Println(k) 137 | fmt.Println(strings.Join(v, "")) 138 | 139 | data := strings.Join(v, "") 140 | 141 | c, err := qr.Encode(data, qr.L) 142 | if err != nil { 143 | fmt.Println(err) 144 | } 145 | pngdat := c.PNG() 146 | 147 | w.Header().Set("Content-Type", "image/png") 148 | w.Write(pngdat) 149 | 150 | defer func() { 151 | vaild = true 152 | }() 153 | } 154 | 155 | } 156 | 157 | if vaild == false { 158 | fmt.Fprintf(w, "Please input data using get method!") 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /pkg/gf256/gf256.go: -------------------------------------------------------------------------------- 1 | // Package gf256 implements arithmetic over the Galois Field GF(256). 2 | package gf256 3 | 4 | import "strconv" 5 | 6 | // A Field represents an instance of GF(256) defined by a specific polynomial. 7 | type Field struct { 8 | log [256]byte // log[0] is unused 9 | exp [510]byte 10 | } 11 | 12 | // NewField returns a new field corresponding to the polynomial poly 13 | // and generator α. The Reed-Solomon encoding in QR codes uses 14 | // polynomial 0x11d with generator 2. 15 | // 16 | // The choice of generator α only affects the Exp and Log operations. 17 | func NewField(poly, α int) *Field { 18 | if poly < 0x100 || poly >= 0x200 || reducible(poly) { 19 | panic("gf256: invalid polynomial: " + strconv.Itoa(poly)) 20 | } 21 | 22 | var f Field 23 | x := 1 24 | for i := 0; i < 255; i++ { 25 | if x == 1 && i != 0 { 26 | panic("gf256: invalid generator " + strconv.Itoa(α) + 27 | " for polynomial " + strconv.Itoa(poly)) 28 | } 29 | f.exp[i] = byte(x) 30 | f.exp[i+255] = byte(x) 31 | f.log[x] = byte(i) 32 | x = mul(x, α, poly) 33 | } 34 | f.log[0] = 255 35 | for i := 0; i < 255; i++ { 36 | if f.log[f.exp[i]] != byte(i) { 37 | panic("bad log") 38 | } 39 | if f.log[f.exp[i+255]] != byte(i) { 40 | panic("bad log") 41 | } 42 | } 43 | for i := 1; i < 256; i++ { 44 | if f.exp[f.log[i]] != byte(i) { 45 | panic("bad log") 46 | } 47 | } 48 | 49 | return &f 50 | } 51 | 52 | // nbit returns the number of significant in p. 53 | func nbit(p int) uint { 54 | n := uint(0) 55 | for ; p > 0; p >>= 1 { 56 | n++ 57 | } 58 | return n 59 | } 60 | 61 | // polyDiv divides the polynomial p by q and returns the remainder. 62 | func polyDiv(p, q int) int { 63 | np := nbit(p) 64 | nq := nbit(q) 65 | for ; np >= nq; np-- { 66 | if p&(1<<(np-1)) != 0 { 67 | p ^= q << (np - nq) 68 | } 69 | } 70 | return p 71 | } 72 | 73 | // mul returns the product x*y mod poly, a GF(256) multiplication. 74 | func mul(x, y, poly int) int { 75 | z := 0 76 | for x > 0 { 77 | if x&1 != 0 { 78 | z ^= y 79 | } 80 | x >>= 1 81 | y <<= 1 82 | if y&0x100 != 0 { 83 | y ^= poly 84 | } 85 | } 86 | return z 87 | } 88 | 89 | // reducible reports whether p is reducible. 90 | func reducible(p int) bool { 91 | // Multiplying n-bit * n-bit produces (2n-1)-bit, 92 | // so if p is reducible, one of its factors must be 93 | // of np/2+1 bits or fewer. 94 | np := nbit(p) 95 | for q := 2; q < 1<<(np/2+1); q++ { 96 | if polyDiv(p, q) == 0 { 97 | return true 98 | } 99 | } 100 | return false 101 | } 102 | 103 | // Add returns the sum of x and y in the field. 104 | func (f *Field) Add(x, y byte) byte { 105 | return x ^ y 106 | } 107 | 108 | // Exp returns the the base-α exponential of e in the field. 109 | // If e < 0, Exp returns 0. 110 | func (f *Field) Exp(e int) byte { 111 | if e < 0 { 112 | return 0 113 | } 114 | return f.exp[e%255] 115 | } 116 | 117 | // Log returns the base-α logarithm of x in the field. 118 | // If x == 0, Log returns -1. 119 | func (f *Field) Log(x byte) int { 120 | if x == 0 { 121 | return -1 122 | } 123 | return int(f.log[x]) 124 | } 125 | 126 | // Inv returns the multiplicative inverse of x in the field. 127 | // If x == 0, Inv returns 0. 128 | func (f *Field) Inv(x byte) byte { 129 | if x == 0 { 130 | return 0 131 | } 132 | return f.exp[255-f.log[x]] 133 | } 134 | 135 | // Mul returns the product of x and y in the field. 136 | func (f *Field) Mul(x, y byte) byte { 137 | if x == 0 || y == 0 { 138 | return 0 139 | } 140 | return f.exp[int(f.log[x])+int(f.log[y])] 141 | } 142 | 143 | // An RSEncoder implements Reed-Solomon encoding 144 | // over a given field using a given number of error correction bytes. 145 | type RSEncoder struct { 146 | f *Field 147 | c int 148 | gen []byte 149 | lgen []byte 150 | p []byte 151 | } 152 | 153 | func (f *Field) gen(e int) (gen, lgen []byte) { 154 | // p = 1 155 | p := make([]byte, e+1) 156 | p[e] = 1 157 | 158 | for i := 0; i < e; i++ { 159 | // p *= (x + Exp(i)) 160 | // p[j] = p[j]*Exp(i) + p[j+1]. 161 | c := f.Exp(i) 162 | for j := 0; j < e; j++ { 163 | p[j] = f.Mul(p[j], c) ^ p[j+1] 164 | } 165 | p[e] = f.Mul(p[e], c) 166 | } 167 | 168 | // lp = log p. 169 | lp := make([]byte, e+1) 170 | for i, c := range p { 171 | if c == 0 { 172 | lp[i] = 255 173 | } else { 174 | lp[i] = byte(f.Log(c)) 175 | } 176 | } 177 | 178 | return p, lp 179 | } 180 | 181 | // NewRSEncoder returns a new Reed-Solomon encoder 182 | // over the given field and number of error correction bytes. 183 | func NewRSEncoder(f *Field, c int) *RSEncoder { 184 | gen, lgen := f.gen(c) 185 | return &RSEncoder{f: f, c: c, gen: gen, lgen: lgen} 186 | } 187 | 188 | // ECC writes to check the error correcting code bytes 189 | // for data using the given Reed-Solomon parameters. 190 | func (rs *RSEncoder) ECC(data []byte, check []byte) { 191 | if len(check) < rs.c { 192 | panic("gf256: invalid check byte length") 193 | } 194 | if rs.c == 0 { 195 | return 196 | } 197 | 198 | // The check bytes are the remainder after dividing 199 | // data padded with c zeros by the generator polynomial. 200 | 201 | // p = data padded with c zeros. 202 | var p []byte 203 | n := len(data) + rs.c 204 | if len(rs.p) >= n { 205 | p = rs.p 206 | } else { 207 | p = make([]byte, n) 208 | } 209 | copy(p, data) 210 | for i := len(data); i < len(p); i++ { 211 | p[i] = 0 212 | } 213 | 214 | // Divide p by gen, leaving the remainder in p[len(data):]. 215 | // p[0] is the most significant term in p, and 216 | // gen[0] is the most significant term in the generator, 217 | // which is always 1. 218 | // To avoid repeated work, we store various values as 219 | // lv, not v, where lv = log[v]. 220 | f := rs.f 221 | lgen := rs.lgen[1:] 222 | for i := 0; i < len(data); i++ { 223 | c := p[i] 224 | if c == 0 { 225 | continue 226 | } 227 | q := p[i+1:] 228 | exp := f.exp[f.log[c]:] 229 | for j, lg := range lgen { 230 | if lg != 255 { // lgen uses 255 for log 0 231 | q[j] ^= exp[lg] 232 | } 233 | } 234 | } 235 | copy(check, p[len(data):]) 236 | rs.p = p 237 | } 238 | -------------------------------------------------------------------------------- /pkg/png.go: -------------------------------------------------------------------------------- 1 | package qr 2 | 3 | // PNG writer for QR codes. 4 | 5 | import ( 6 | "bytes" 7 | "encoding/binary" 8 | "hash" 9 | "hash/crc32" 10 | ) 11 | 12 | // PNG returns a PNG image displaying the code. 13 | // 14 | // PNG uses a custom encoder tailored to QR codes. 15 | // Its compressed size is about 2x away from optimal, 16 | // but it runs about 20x faster than calling png.Encode 17 | // on c.Image(). 18 | func (c *Code) PNG() []byte { 19 | var p pngWriter 20 | return p.encode(c) 21 | } 22 | 23 | type pngWriter struct { 24 | tmp [16]byte 25 | wctmp [4]byte 26 | buf bytes.Buffer 27 | zlib bitWriter 28 | crc hash.Hash32 29 | } 30 | 31 | var pngHeader = []byte("\x89PNG\r\n\x1a\n") 32 | 33 | func (w *pngWriter) encode(c *Code) []byte { 34 | scale := c.Scale 35 | siz := c.Size 36 | 37 | w.buf.Reset() 38 | 39 | // Header 40 | w.buf.Write(pngHeader) 41 | 42 | // Header block 43 | binary.BigEndian.PutUint32(w.tmp[0:4], uint32((siz+8)*scale)) 44 | binary.BigEndian.PutUint32(w.tmp[4:8], uint32((siz+8)*scale)) 45 | w.tmp[8] = 1 // 1-bit 46 | w.tmp[9] = 0 // gray 47 | w.tmp[10] = 0 48 | w.tmp[11] = 0 49 | w.tmp[12] = 0 50 | w.writeChunk("IHDR", w.tmp[:13]) 51 | 52 | // Comment 53 | w.writeChunk("tEXt", comment) 54 | 55 | // Data 56 | w.zlib.writeCode(c) 57 | w.writeChunk("IDAT", w.zlib.bytes.Bytes()) 58 | 59 | // End 60 | w.writeChunk("IEND", nil) 61 | 62 | return w.buf.Bytes() 63 | } 64 | 65 | var comment = []byte("Software\x00QR-PNG http://qr.swtch.com/") 66 | 67 | func (w *pngWriter) writeChunk(name string, data []byte) { 68 | if w.crc == nil { 69 | w.crc = crc32.NewIEEE() 70 | } 71 | binary.BigEndian.PutUint32(w.wctmp[0:4], uint32(len(data))) 72 | w.buf.Write(w.wctmp[0:4]) 73 | w.crc.Reset() 74 | copy(w.wctmp[0:4], name) 75 | w.buf.Write(w.wctmp[0:4]) 76 | w.crc.Write(w.wctmp[0:4]) 77 | w.buf.Write(data) 78 | w.crc.Write(data) 79 | crc := w.crc.Sum32() 80 | binary.BigEndian.PutUint32(w.wctmp[0:4], crc) 81 | w.buf.Write(w.wctmp[0:4]) 82 | } 83 | 84 | func (b *bitWriter) writeCode(c *Code) { 85 | const ftNone = 0 86 | 87 | b.adler32.Reset() 88 | b.bytes.Reset() 89 | b.nbit = 0 90 | 91 | scale := c.Scale 92 | siz := c.Size 93 | 94 | // zlib header 95 | b.tmp[0] = 0x78 96 | b.tmp[1] = 0 97 | b.tmp[1] += uint8(31 - (uint16(b.tmp[0])<<8+uint16(b.tmp[1]))%31) 98 | b.bytes.Write(b.tmp[0:2]) 99 | 100 | // Start flate block. 101 | b.writeBits(1, 1, false) // final block 102 | b.writeBits(1, 2, false) // compressed, fixed Huffman tables 103 | 104 | // White border. 105 | // First row. 106 | b.byte(ftNone) 107 | n := (scale*(siz+8) + 7) / 8 108 | b.byte(255) 109 | b.repeat(n-1, 1) 110 | // 4*scale rows total. 111 | b.repeat((4*scale-1)*(1+n), 1+n) 112 | 113 | for i := 0; i < 4*scale; i++ { 114 | b.adler32.WriteNByte(ftNone, 1) 115 | b.adler32.WriteNByte(255, n) 116 | } 117 | 118 | row := make([]byte, 1+n) 119 | for y := 0; y < siz; y++ { 120 | row[0] = ftNone 121 | j := 1 122 | var z uint8 123 | nz := 0 124 | for x := -4; x < siz+4; x++ { 125 | // Raw data. 126 | for i := 0; i < scale; i++ { 127 | z <<= 1 128 | if !c.Black(x, y) { 129 | z |= 1 130 | } 131 | if nz++; nz == 8 { 132 | row[j] = z 133 | j++ 134 | nz = 0 135 | } 136 | } 137 | } 138 | if j < len(row) { 139 | row[j] = z 140 | } 141 | for _, z := range row { 142 | b.byte(z) 143 | } 144 | 145 | // Scale-1 copies. 146 | b.repeat((scale-1)*(1+n), 1+n) 147 | 148 | b.adler32.WriteN(row, scale) 149 | } 150 | 151 | // White border. 152 | // First row. 153 | b.byte(ftNone) 154 | b.byte(255) 155 | b.repeat(n-1, 1) 156 | // 4*scale rows total. 157 | b.repeat((4*scale-1)*(1+n), 1+n) 158 | 159 | for i := 0; i < 4*scale; i++ { 160 | b.adler32.WriteNByte(ftNone, 1) 161 | b.adler32.WriteNByte(255, n) 162 | } 163 | 164 | // End of block. 165 | b.hcode(256) 166 | b.flushBits() 167 | 168 | // adler32 169 | binary.BigEndian.PutUint32(b.tmp[0:], b.adler32.Sum32()) 170 | b.bytes.Write(b.tmp[0:4]) 171 | } 172 | 173 | // A bitWriter is a write buffer for bit-oriented data like deflate. 174 | type bitWriter struct { 175 | bytes bytes.Buffer 176 | bit uint32 177 | nbit uint 178 | 179 | tmp [4]byte 180 | adler32 adigest 181 | } 182 | 183 | func (b *bitWriter) writeBits(bit uint32, nbit uint, rev bool) { 184 | // reverse, for huffman codes 185 | if rev { 186 | br := uint32(0) 187 | for i := uint(0); i < nbit; i++ { 188 | br |= ((bit >> i) & 1) << (nbit - 1 - i) 189 | } 190 | bit = br 191 | } 192 | b.bit |= bit << b.nbit 193 | b.nbit += nbit 194 | for b.nbit >= 8 { 195 | b.bytes.WriteByte(byte(b.bit)) 196 | b.bit >>= 8 197 | b.nbit -= 8 198 | } 199 | } 200 | 201 | func (b *bitWriter) flushBits() { 202 | if b.nbit > 0 { 203 | b.bytes.WriteByte(byte(b.bit)) 204 | b.nbit = 0 205 | b.bit = 0 206 | } 207 | } 208 | 209 | func (b *bitWriter) hcode(v int) { 210 | /* 211 | Lit Value Bits Codes 212 | --------- ---- ----- 213 | 0 - 143 8 00110000 through 214 | 10111111 215 | 144 - 255 9 110010000 through 216 | 111111111 217 | 256 - 279 7 0000000 through 218 | 0010111 219 | 280 - 287 8 11000000 through 220 | 11000111 221 | */ 222 | switch { 223 | case v <= 143: 224 | b.writeBits(uint32(v)+0x30, 8, true) 225 | case v <= 255: 226 | b.writeBits(uint32(v-144)+0x190, 9, true) 227 | case v <= 279: 228 | b.writeBits(uint32(v-256)+0, 7, true) 229 | case v <= 287: 230 | b.writeBits(uint32(v-280)+0xc0, 8, true) 231 | default: 232 | panic("invalid hcode") 233 | } 234 | } 235 | 236 | func (b *bitWriter) byte(x byte) { 237 | b.hcode(int(x)) 238 | } 239 | 240 | func (b *bitWriter) codex(c int, val int, nx uint) { 241 | b.hcode(c + val>>nx) 242 | b.writeBits(uint32(val)&(1<= 258+3; n -= 258 { 247 | b.repeat1(258, d) 248 | } 249 | if n > 258 { 250 | // 258 < n < 258+3 251 | b.repeat1(10, d) 252 | b.repeat1(n-10, d) 253 | return 254 | } 255 | if n < 3 { 256 | panic("invalid flate repeat") 257 | } 258 | b.repeat1(n, d) 259 | } 260 | 261 | func (b *bitWriter) repeat1(n, d int) { 262 | /* 263 | Extra Extra Extra 264 | Code Bits Length(s) Code Bits Lengths Code Bits Length(s) 265 | ---- ---- ------ ---- ---- ------- ---- ---- ------- 266 | 257 0 3 267 1 15,16 277 4 67-82 267 | 258 0 4 268 1 17,18 278 4 83-98 268 | 259 0 5 269 2 19-22 279 4 99-114 269 | 260 0 6 270 2 23-26 280 4 115-130 270 | 261 0 7 271 2 27-30 281 5 131-162 271 | 262 0 8 272 2 31-34 282 5 163-194 272 | 263 0 9 273 3 35-42 283 5 195-226 273 | 264 0 10 274 3 43-50 284 5 227-257 274 | 265 1 11,12 275 3 51-58 285 0 258 275 | 266 1 13,14 276 3 59-66 276 | */ 277 | switch { 278 | case n <= 10: 279 | b.codex(257, n-3, 0) 280 | case n <= 18: 281 | b.codex(265, n-11, 1) 282 | case n <= 34: 283 | b.codex(269, n-19, 2) 284 | case n <= 66: 285 | b.codex(273, n-35, 3) 286 | case n <= 130: 287 | b.codex(277, n-67, 4) 288 | case n <= 257: 289 | b.codex(281, n-131, 5) 290 | case n == 258: 291 | b.hcode(285) 292 | default: 293 | panic("invalid repeat length") 294 | } 295 | 296 | /* 297 | Extra Extra Extra 298 | Code Bits Dist Code Bits Dist Code Bits Distance 299 | ---- ---- ---- ---- ---- ------ ---- ---- -------- 300 | 0 0 1 10 4 33-48 20 9 1025-1536 301 | 1 0 2 11 4 49-64 21 9 1537-2048 302 | 2 0 3 12 5 65-96 22 10 2049-3072 303 | 3 0 4 13 5 97-128 23 10 3073-4096 304 | 4 1 5,6 14 6 129-192 24 11 4097-6144 305 | 5 1 7,8 15 6 193-256 25 11 6145-8192 306 | 6 2 9-12 16 7 257-384 26 12 8193-12288 307 | 7 2 13-16 17 7 385-512 27 12 12289-16384 308 | 8 3 17-24 18 8 513-768 28 13 16385-24576 309 | 9 3 25-32 19 8 769-1024 29 13 24577-32768 310 | */ 311 | if d <= 4 { 312 | b.writeBits(uint32(d-1), 5, true) 313 | } else if d <= 32768 { 314 | nbit := uint(16) 315 | for d <= 1<<(nbit-1) { 316 | nbit-- 317 | } 318 | v := uint32(d - 1) 319 | v &^= 1 << (nbit - 1) // top bit is implicit 320 | code := uint32(2*nbit - 2) // second bit is low bit of code 321 | code |= v >> (nbit - 2) 322 | v &^= 1 << (nbit - 2) 323 | b.writeBits(code, 5, true) 324 | // rest of bits follow 325 | b.writeBits(uint32(v), nbit-2, false) 326 | } else { 327 | panic("invalid repeat distance") 328 | } 329 | } 330 | 331 | func (b *bitWriter) run(v byte, n int) { 332 | if n == 0 { 333 | return 334 | } 335 | b.byte(v) 336 | if n-1 < 3 { 337 | for i := 0; i < n-1; i++ { 338 | b.byte(v) 339 | } 340 | } else { 341 | b.repeat(n-1, 1) 342 | } 343 | } 344 | 345 | type adigest struct { 346 | a, b uint32 347 | } 348 | 349 | func (d *adigest) Reset() { d.a, d.b = 1, 0 } 350 | 351 | const amod = 65521 352 | 353 | func aupdate(a, b uint32, pi byte, n int) (aa, bb uint32) { 354 | // TODO(rsc): 6g doesn't do magic multiplies for b %= amod, 355 | // only for b = b%amod. 356 | 357 | // invariant: a, b < amod 358 | if pi == 0 { 359 | b += uint32(n%amod) * a 360 | b = b % amod 361 | return a, b 362 | } 363 | 364 | // n times: 365 | // a += pi 366 | // b += a 367 | // is same as 368 | // b += n*a + n*(n+1)/2*pi 369 | // a += n*pi 370 | m := uint32(n) 371 | b += (m % amod) * a 372 | b = b % amod 373 | b += (m * (m + 1) / 2) % amod * uint32(pi) 374 | b = b % amod 375 | a += (m % amod) * uint32(pi) 376 | a = a % amod 377 | return a, b 378 | } 379 | 380 | func afinish(a, b uint32) uint32 { 381 | return b<<16 | a 382 | } 383 | 384 | func (d *adigest) WriteN(p []byte, n int) { 385 | for i := 0; i < n; i++ { 386 | for _, pi := range p { 387 | d.a, d.b = aupdate(d.a, d.b, pi, 1) 388 | } 389 | } 390 | } 391 | 392 | func (d *adigest) WriteNByte(pi byte, n int) { 393 | d.a, d.b = aupdate(d.a, d.b, pi, n) 394 | } 395 | 396 | func (d *adigest) Sum32() uint32 { return afinish(d.a, d.b) } 397 | -------------------------------------------------------------------------------- /pkg/coding/qr.go: -------------------------------------------------------------------------------- 1 | package coding 2 | 3 | import ( 4 | "fmt" 5 | "github.com/RaymondChou/goqr/pkg/gf256" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // Field is the field for QR error correction. 11 | var Field = gf256.NewField(0x11d, 2) 12 | 13 | // A Version represents a QR version. 14 | // The version specifies the size of the QR code: 15 | // a QR code with version v has 4v+17 pixels on a side. 16 | // Versions number from 1 to 40: the larger the version, 17 | // the more information the code can store. 18 | type Version int 19 | 20 | const MinVersion = 1 21 | const MaxVersion = 40 22 | 23 | func (v Version) String() string { 24 | return strconv.Itoa(int(v)) 25 | } 26 | 27 | func (v Version) sizeClass() int { 28 | if v <= 9 { 29 | return 0 30 | } 31 | if v <= 26 { 32 | return 1 33 | } 34 | return 2 35 | } 36 | 37 | // DataBytes returns the number of data bytes that can be 38 | // stored in a QR code with the given version and level. 39 | func (v Version) DataBytes(l Level) int { 40 | vt := &vtab[v] 41 | lev := &vt.level[l] 42 | return vt.bytes - lev.nblock*lev.check 43 | } 44 | 45 | // Encoding implements a QR data encoding scheme. 46 | // The implementations--Numeric, Alphanumeric, and String--specify 47 | // the character set and the mapping from UTF-8 to code bits. 48 | // The more restrictive the mode, the fewer code bits are needed. 49 | type Encoding interface { 50 | Check() error 51 | Bits(v Version) int 52 | Encode(b *Bits, v Version) 53 | } 54 | 55 | type Bits struct { 56 | b []byte 57 | nbit int 58 | } 59 | 60 | func (b *Bits) Reset() { 61 | b.b = b.b[:0] 62 | b.nbit = 0 63 | } 64 | 65 | func (b *Bits) Bits() int { 66 | return b.nbit 67 | } 68 | 69 | func (b *Bits) Bytes() []byte { 70 | if b.nbit%8 != 0 { 71 | panic("fractional byte") 72 | } 73 | return b.b 74 | } 75 | 76 | func (b *Bits) Append(p []byte) { 77 | if b.nbit%8 != 0 { 78 | panic("fractional byte") 79 | } 80 | b.b = append(b.b, p...) 81 | b.nbit += 8 * len(p) 82 | } 83 | 84 | func (b *Bits) Write(v uint, nbit int) { 85 | for nbit > 0 { 86 | n := nbit 87 | if n > 8 { 88 | n = 8 89 | } 90 | if b.nbit%8 == 0 { 91 | b.b = append(b.b, 0) 92 | } else { 93 | m := -b.nbit & 7 94 | if n > m { 95 | n = m 96 | } 97 | } 98 | b.nbit += n 99 | sh := uint(nbit - n) 100 | b.b[len(b.b)-1] |= uint8(v >> sh << uint(-b.nbit&7)) 101 | v -= v >> sh << sh 102 | nbit -= n 103 | } 104 | } 105 | 106 | // Num is the encoding for numeric data. 107 | // The only valid characters are the decimal digits 0 through 9. 108 | type Num string 109 | 110 | func (s Num) String() string { 111 | return fmt.Sprintf("Num(%#q)", string(s)) 112 | } 113 | 114 | func (s Num) Check() error { 115 | for _, c := range s { 116 | if c < '0' || '9' < c { 117 | return fmt.Errorf("non-numeric string %#q", string(s)) 118 | } 119 | } 120 | return nil 121 | } 122 | 123 | var numLen = [3]int{10, 12, 14} 124 | 125 | func (s Num) Bits(v Version) int { 126 | return 4 + numLen[v.sizeClass()] + (10*len(s)+2)/3 127 | } 128 | 129 | func (s Num) Encode(b *Bits, v Version) { 130 | b.Write(1, 4) 131 | b.Write(uint(len(s)), numLen[v.sizeClass()]) 132 | var i int 133 | for i = 0; i+3 <= len(s); i += 3 { 134 | w := uint(s[i]-'0')*100 + uint(s[i+1]-'0')*10 + uint(s[i+2]-'0') 135 | b.Write(w, 10) 136 | } 137 | switch len(s) - i { 138 | case 1: 139 | w := uint(s[i] - '0') 140 | b.Write(w, 4) 141 | case 2: 142 | w := uint(s[i]-'0')*10 + uint(s[i+1]-'0') 143 | b.Write(w, 7) 144 | } 145 | } 146 | 147 | // Alpha is the encoding for alphanumeric data. 148 | // The valid characters are 0-9A-Z$%*+-./: and space. 149 | type Alpha string 150 | 151 | const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:" 152 | 153 | func (s Alpha) String() string { 154 | return fmt.Sprintf("Alpha(%#q)", string(s)) 155 | } 156 | 157 | func (s Alpha) Check() error { 158 | for _, c := range s { 159 | if strings.IndexRune(alphabet, c) < 0 { 160 | return fmt.Errorf("non-alphanumeric string %#q", string(s)) 161 | } 162 | } 163 | return nil 164 | } 165 | 166 | var alphaLen = [3]int{9, 11, 13} 167 | 168 | func (s Alpha) Bits(v Version) int { 169 | return 4 + alphaLen[v.sizeClass()] + (11*len(s)+1)/2 170 | } 171 | 172 | func (s Alpha) Encode(b *Bits, v Version) { 173 | b.Write(2, 4) 174 | b.Write(uint(len(s)), alphaLen[v.sizeClass()]) 175 | var i int 176 | for i = 0; i+2 <= len(s); i += 2 { 177 | w := uint(strings.IndexRune(alphabet, rune(s[i])))*45 + 178 | uint(strings.IndexRune(alphabet, rune(s[i+1]))) 179 | b.Write(w, 11) 180 | } 181 | 182 | if i < len(s) { 183 | w := uint(strings.IndexRune(alphabet, rune(s[i]))) 184 | b.Write(w, 6) 185 | } 186 | } 187 | 188 | // String is the encoding for 8-bit data. All bytes are valid. 189 | type String string 190 | 191 | func (s String) String() string { 192 | return fmt.Sprintf("String(%#q)", string(s)) 193 | } 194 | 195 | func (s String) Check() error { 196 | return nil 197 | } 198 | 199 | var stringLen = [3]int{8, 16, 16} 200 | 201 | func (s String) Bits(v Version) int { 202 | return 4 + stringLen[v.sizeClass()] + 8*len(s) 203 | } 204 | 205 | func (s String) Encode(b *Bits, v Version) { 206 | b.Write(4, 4) 207 | b.Write(uint(len(s)), stringLen[v.sizeClass()]) 208 | for i := 0; i < len(s); i++ { 209 | b.Write(uint(s[i]), 8) 210 | } 211 | } 212 | 213 | // A Pixel describes a single pixel in a QR code. 214 | type Pixel uint32 215 | 216 | const ( 217 | Black Pixel = 1 << iota 218 | Invert 219 | ) 220 | 221 | func (p Pixel) Offset() uint { 222 | return uint(p >> 6) 223 | } 224 | 225 | func OffsetPixel(o uint) Pixel { 226 | return Pixel(o << 6) 227 | } 228 | 229 | func (r PixelRole) Pixel() Pixel { 230 | return Pixel(r << 2) 231 | } 232 | 233 | func (p Pixel) Role() PixelRole { 234 | return PixelRole(p>>2) & 15 235 | } 236 | 237 | func (p Pixel) String() string { 238 | s := p.Role().String() 239 | if p&Black != 0 { 240 | s += "+black" 241 | } 242 | if p&Invert != 0 { 243 | s += "+invert" 244 | } 245 | s += "+" + strconv.FormatUint(uint64(p.Offset()), 10) 246 | return s 247 | } 248 | 249 | // A PixelRole describes the role of a QR pixel. 250 | type PixelRole uint32 251 | 252 | const ( 253 | _ PixelRole = iota 254 | Position // position squares (large) 255 | Alignment // alignment squares (small) 256 | Timing // timing strip between position squares 257 | Format // format metadata 258 | PVersion // version pattern 259 | Unused // unused pixel 260 | Data // data bit 261 | Check // error correction check bit 262 | Extra 263 | ) 264 | 265 | var roles = []string{ 266 | "", 267 | "position", 268 | "alignment", 269 | "timing", 270 | "format", 271 | "pversion", 272 | "unused", 273 | "data", 274 | "check", 275 | "extra", 276 | } 277 | 278 | func (r PixelRole) String() string { 279 | if Position <= r && r <= Check { 280 | return roles[r] 281 | } 282 | return strconv.Itoa(int(r)) 283 | } 284 | 285 | // A Level represents a QR error correction level. 286 | // From least to most tolerant of errors, they are L, M, Q, H. 287 | type Level int 288 | 289 | const ( 290 | L Level = iota 291 | M 292 | Q 293 | H 294 | ) 295 | 296 | func (l Level) String() string { 297 | if L <= l && l <= H { 298 | return "LMQH"[l : l+1] 299 | } 300 | return strconv.Itoa(int(l)) 301 | } 302 | 303 | // A Code is a square pixel grid. 304 | type Code struct { 305 | Bitmap []byte // 1 is black, 0 is white 306 | Size int // number of pixels on a side 307 | Stride int // number of bytes per row 308 | } 309 | 310 | func (c *Code) Black(x, y int) bool { 311 | return 0 <= x && x < c.Size && 0 <= y && y < c.Size && 312 | c.Bitmap[y*c.Stride+x/8]&(1<= pad { 388 | break 389 | } 390 | b.Write(0x11, 8) 391 | } 392 | } 393 | } 394 | 395 | func (b *Bits) AddCheckBytes(v Version, l Level) { 396 | nd := v.DataBytes(l) 397 | if b.nbit < nd*8 { 398 | b.Pad(nd*8 - b.nbit) 399 | } 400 | if b.nbit != nd*8 { 401 | panic("qr: too much data") 402 | } 403 | 404 | dat := b.Bytes() 405 | vt := &vtab[v] 406 | lev := &vt.level[l] 407 | db := nd / lev.nblock 408 | extra := nd % lev.nblock 409 | chk := make([]byte, lev.check) 410 | rs := gf256.NewRSEncoder(Field, lev.check) 411 | for i := 0; i < lev.nblock; i++ { 412 | if i == lev.nblock-extra { 413 | db++ 414 | } 415 | rs.ECC(dat[:db], chk) 416 | b.Append(chk) 417 | dat = dat[db:] 418 | } 419 | 420 | if len(b.Bytes()) != vt.bytes { 421 | panic("qr: internal error") 422 | } 423 | } 424 | 425 | func (p *Plan) Encode(text ...Encoding) (*Code, error) { 426 | var b Bits 427 | for _, t := range text { 428 | if err := t.Check(); err != nil { 429 | return nil, err 430 | } 431 | t.Encode(&b, p.Version) 432 | } 433 | if b.Bits() > p.DataBytes*8 { 434 | return nil, fmt.Errorf("cannot encode %d bits into %d-bit code", b.Bits(), p.DataBytes*8) 435 | } 436 | b.AddCheckBytes(p.Version, p.Level) 437 | bytes := b.Bytes() 438 | 439 | // Now we have the checksum bytes and the data bytes. 440 | // Construct the actual code. 441 | c := &Code{Size: len(p.Pixel), Stride: (len(p.Pixel) + 7) &^ 7} 442 | c.Bitmap = make([]byte, c.Stride*c.Size) 443 | crow := c.Bitmap 444 | for _, row := range p.Pixel { 445 | for x, pix := range row { 446 | switch pix.Role() { 447 | case Data, Check: 448 | o := pix.Offset() 449 | if bytes[o/8]&(1< 40 { 533 | return nil, fmt.Errorf("invalid QR version %d", int(v)) 534 | } 535 | siz := 17 + int(v)*4 536 | m := grid(siz) 537 | p.Pixel = m 538 | 539 | // Timing markers (overwritten by boxes). 540 | const ti = 6 // timing is in row/column 6 (counting from 0) 541 | for i := range m { 542 | p := Timing.Pixel() 543 | if i&1 == 0 { 544 | p |= Black 545 | } 546 | m[i][ti] = p 547 | m[ti][i] = p 548 | } 549 | 550 | // Position boxes. 551 | posBox(m, 0, 0) 552 | posBox(m, siz-7, 0) 553 | posBox(m, 0, siz-7) 554 | 555 | // Alignment boxes. 556 | info := &vtab[v] 557 | for x := 4; x+5 < siz; { 558 | for y := 4; y+5 < siz; { 559 | // don't overwrite timing markers 560 | if (x < 7 && y < 7) || (x < 7 && y+5 >= siz-7) || (x+5 >= siz-7 && y < 7) { 561 | } else { 562 | alignBox(m, x, y) 563 | } 564 | if y == 4 { 565 | y = info.apos 566 | } else { 567 | y += info.astride 568 | } 569 | } 570 | if x == 4 { 571 | x = info.apos 572 | } else { 573 | x += info.astride 574 | } 575 | } 576 | 577 | // Version pattern. 578 | pat := vtab[v].pattern 579 | if pat != 0 { 580 | v := pat 581 | for x := 0; x < 6; x++ { 582 | for y := 0; y < 3; y++ { 583 | p := PVersion.Pixel() 584 | if v&1 != 0 { 585 | p |= Black 586 | } 587 | m[siz-11+y][x] = p 588 | m[x][siz-11+y] = p 589 | v >>= 1 590 | } 591 | } 592 | } 593 | 594 | // One lonely black pixel 595 | m[siz-8][8] = Unused.Pixel() | Black 596 | 597 | return p, nil 598 | } 599 | 600 | // fplan adds the format pixels 601 | func fplan(l Level, m Mask, p *Plan) error { 602 | // Format pixels. 603 | fb := uint32(l^1) << 13 // level: L=01, M=00, Q=11, H=10 604 | fb |= uint32(m) << 10 // mask 605 | const formatPoly = 0x537 606 | rem := fb 607 | for i := 14; i >= 10; i-- { 608 | if rem&(1<>i)&1 == 1 { 618 | pix |= Black 619 | } 620 | if (invert>>i)&1 == 1 { 621 | pix ^= Invert | Black 622 | } 623 | // top left 624 | switch { 625 | case i < 6: 626 | p.Pixel[i][8] = pix 627 | case i < 8: 628 | p.Pixel[i+1][8] = pix 629 | case i < 9: 630 | p.Pixel[8][7] = pix 631 | default: 632 | p.Pixel[8][14-i] = pix 633 | } 634 | // bottom right 635 | switch { 636 | case i < 8: 637 | p.Pixel[8][siz-1-int(i)] = pix 638 | default: 639 | p.Pixel[siz-1-int(14-i)][8] = pix 640 | } 641 | } 642 | return nil 643 | } 644 | 645 | // lplan edits a version-only Plan to add information 646 | // about the error correction levels. 647 | func lplan(v Version, l Level, p *Plan) error { 648 | p.Level = l 649 | 650 | nblock := vtab[v].level[l].nblock 651 | ne := vtab[v].level[l].check 652 | nde := (vtab[v].bytes - ne*nblock) / nblock 653 | extra := (vtab[v].bytes - ne*nblock) % nblock 654 | dataBits := (nde*nblock + extra) * 8 655 | checkBits := ne * nblock * 8 656 | 657 | p.DataBytes = vtab[v].bytes - ne*nblock 658 | p.CheckBytes = ne * nblock 659 | p.Blocks = nblock 660 | 661 | // Make data + checksum pixels. 662 | data := make([]Pixel, dataBits) 663 | for i := range data { 664 | data[i] = Data.Pixel() | OffsetPixel(uint(i)) 665 | } 666 | check := make([]Pixel, checkBits) 667 | for i := range check { 668 | check[i] = Check.Pixel() | OffsetPixel(uint(i+dataBits)) 669 | } 670 | 671 | // Split into blocks. 672 | dataList := make([][]Pixel, nblock) 673 | checkList := make([][]Pixel, nblock) 674 | for i := 0; i < nblock; i++ { 675 | // The last few blocks have an extra data byte (8 pixels). 676 | nd := nde 677 | if i >= nblock-extra { 678 | nd++ 679 | } 680 | dataList[i], data = data[0:nd*8], data[nd*8:] 681 | checkList[i], check = check[0:ne*8], check[ne*8:] 682 | } 683 | if len(data) != 0 || len(check) != 0 { 684 | panic("data/check math") 685 | } 686 | 687 | // Build up bit sequence, taking first byte of each block, 688 | // then second byte, and so on. Then checksums. 689 | bits := make([]Pixel, dataBits+checkBits) 690 | dst := bits 691 | for i := 0; i < nde+1; i++ { 692 | for _, b := range dataList { 693 | if i*8 < len(b) { 694 | copy(dst, b[i*8:(i+1)*8]) 695 | dst = dst[8:] 696 | } 697 | } 698 | } 699 | for i := 0; i < ne; i++ { 700 | for _, b := range checkList { 701 | if i*8 < len(b) { 702 | copy(dst, b[i*8:(i+1)*8]) 703 | dst = dst[8:] 704 | } 705 | } 706 | } 707 | if len(dst) != 0 { 708 | panic("dst math") 709 | } 710 | 711 | // Sweep up pair of columns, 712 | // then down, assigning to right then left pixel. 713 | // Repeat. 714 | // See Figure 2 of http://www.pclviewer.com/rs2/qrtopology.htm 715 | siz := len(p.Pixel) 716 | rem := make([]Pixel, 7) 717 | for i := range rem { 718 | rem[i] = Extra.Pixel() 719 | } 720 | src := append(bits, rem...) 721 | for x := siz; x > 0; { 722 | for y := siz - 1; y >= 0; y-- { 723 | if p.Pixel[y][x-1].Role() == 0 { 724 | p.Pixel[y][x-1], src = src[0], src[1:] 725 | } 726 | if p.Pixel[y][x-2].Role() == 0 { 727 | p.Pixel[y][x-2], src = src[0], src[1:] 728 | } 729 | } 730 | x -= 2 731 | if x == 7 { // vertical timing strip 732 | x-- 733 | } 734 | for y := 0; y < siz; y++ { 735 | if p.Pixel[y][x-1].Role() == 0 { 736 | p.Pixel[y][x-1], src = src[0], src[1:] 737 | } 738 | if p.Pixel[y][x-2].Role() == 0 { 739 | p.Pixel[y][x-2], src = src[0], src[1:] 740 | } 741 | } 742 | x -= 2 743 | } 744 | return nil 745 | } 746 | 747 | // mplan edits a version+level-only Plan to add the mask. 748 | func mplan(m Mask, p *Plan) error { 749 | p.Mask = m 750 | for y, row := range p.Pixel { 751 | for x, pix := range row { 752 | if r := pix.Role(); (r == Data || r == Check || r == Extra) && p.Mask.Invert(y, x) { 753 | row[x] ^= Black | Invert 754 | } 755 | } 756 | } 757 | return nil 758 | } 759 | 760 | // posBox draws a position (large) box at upper left x, y. 761 | func posBox(m [][]Pixel, x, y int) { 762 | pos := Position.Pixel() 763 | // box 764 | for dy := 0; dy < 7; dy++ { 765 | for dx := 0; dx < 7; dx++ { 766 | p := pos 767 | if dx == 0 || dx == 6 || dy == 0 || dy == 6 || 2 <= dx && dx <= 4 && 2 <= dy && dy <= 4 { 768 | p |= Black 769 | } 770 | m[y+dy][x+dx] = p 771 | } 772 | } 773 | // white border 774 | for dy := -1; dy < 8; dy++ { 775 | if 0 <= y+dy && y+dy < len(m) { 776 | if x > 0 { 777 | m[y+dy][x-1] = pos 778 | } 779 | if x+7 < len(m) { 780 | m[y+dy][x+7] = pos 781 | } 782 | } 783 | } 784 | for dx := -1; dx < 8; dx++ { 785 | if 0 <= x+dx && x+dx < len(m) { 786 | if y > 0 { 787 | m[y-1][x+dx] = pos 788 | } 789 | if y+7 < len(m) { 790 | m[y+7][x+dx] = pos 791 | } 792 | } 793 | } 794 | } 795 | 796 | // alignBox draw an alignment (small) box at upper left x, y. 797 | func alignBox(m [][]Pixel, x, y int) { 798 | // box 799 | align := Alignment.Pixel() 800 | for dy := 0; dy < 5; dy++ { 801 | for dx := 0; dx < 5; dx++ { 802 | p := align 803 | if dx == 0 || dx == 4 || dy == 0 || dy == 4 || dx == 2 && dy == 2 { 804 | p |= Black 805 | } 806 | m[y+dy][x+dx] = p 807 | } 808 | } 809 | } 810 | --------------------------------------------------------------------------------