package main import ( "fmt" "image" "image/color" "image/png" "os" ) const ( Size = 21 DataCodewords = 19 ECCCodewords = 7 MaxBytesByteMode = 17 ModeIndicatorByte = 0x4 ) var gfExp [512]int var gfLog [256]int func initGFTables() { x := 1 for i := 0; i < 255; i++ { gfExp[i] = x gfLog[x] = i x <<= 1 if x&0x100 != 0 { x ^= 0x11d } } for i := 255; i < 512; i++ { gfExp[i] = gfExp[i-255] } gfLog[0] = -1 } func gfMul(a, b int) int { if a == 0 || b == 0 { return 0 } return gfExp[(gfLog[a]+gfLog[b])%255] } func rsGenerator(n int) []int { g := []int{1} for i := 0; i < n; i++ { newG := make([]int, len(g)+1) for j := range newG { newG[j] = 0 } for j := 0; j < len(g); j++ { newG[j] ^= g[j] newG[j+1] ^= gfMul(g[j], gfExp[i]) } g = newG } return g } func rsComputeECC(data []int, eccLen int, g []int) []int { poly := make([]int, len(data)+eccLen) copy(poly, data) for i := 0; i < len(data); i++ { coef := poly[i] if coef != 0 { for j := 0; j < len(g); j++ { poly[i+j] ^= gfMul(coef, g[j]) } } } return poly[len(data):] } type bitBuffer struct { bits []bool } func (bb *bitBuffer) appendBits(val int, length int) { for i := length - 1; i >= 0; i-- { bb.bits = append(bb.bits, ((val>>i)&1) == 1) } } func encodeBytesToCodewords(data []byte) ([]int, error) { if len(data) > MaxBytesByteMode { return nil, fmt.Errorf("input too long for version 1-L: max %d bytes", MaxBytesByteMode) } var bb bitBuffer bb.appendBits(ModeIndicatorByte, 4) bb.appendBits(len(data), 8) for _, b := range data { bb.appendBits(int(b), 8) } remainingBits := DataCodewords*8 - len(bb.bits) if remainingBits < 0 { return nil, fmt.Errorf("data bits exceed capacity") } if remainingBits >= 4 { bb.appendBits(0, 4) remainingBits -= 4 } else { bb.appendBits(0, remainingBits) remainingBits = 0 } if (len(bb.bits) % 8) != 0 { toPad := 8 - (len(bb.bits) % 8) bb.appendBits(0, toPad) remainingBits -= toPad } padBytes := []int{0xEC, 0x11} idx := 0 for (len(bb.bits) / 8) < DataCodewords { bb.appendBits(padBytes[idx%2], 8) idx++ } codewords := make([]int, DataCodewords) for i := 0; i < DataCodewords; i++ { val := 0 for b := 0; b < 8; b++ { if bb.bits[i*8+b] { val |= 1 << (7 - b) } } codewords[i] = val } return codewords, nil } func newMatrix() [][]int { m := make([][]int, Size) for i := range m { m[i] = make([]int, Size) for j := range m[i] { m[i][j] = -1 } } return m } func placeFinder(m [][]int, r, c int) { p := []string{ "1111111", "1000001", "1011101", "1011101", "1011101", "1000001", "1111111", } for i := 0; i < 7; i++ { for j := 0; j < 7; j++ { if p[i][j] == '1' { m[r+i][c+j] = 1 } else { m[r+i][c+j] = 0 } } } for i := -1; i <= 7; i++ { for j := -1; j <= 7; j++ { rr := r + i cc := c + j if rr < 0 || rr >= Size || cc < 0 || cc >= Size { continue } if (i == -1 || i == 7 || j == -1 || j == 7) && m[rr][cc] == -1 { m[rr][cc] = 0 } } } } func placeTiming(m [][]int) { for i := 8; i < Size-8; i++ { if m[6][i] == -1 { if i%2 == 0 { m[6][i] = 1 } else { m[6][i] = 0 } } if m[i][6] == -1 { if i%2 == 0 { m[i][6] = 1 } else { m[i][6] = 0 } } } } func placeData(m [][]int, dataBits []bool) { dirUp := true row := Size - 1 col := Size - 1 bitIdx := 0 for col > 0 { if col == 6 { col-- } for { for c := col; c >= col-1; c-- { if m[row][c] == -1 { var bit bool if bitIdx < len(dataBits) { bit = dataBits[bitIdx] } else { bit = false } if bit { m[row][c] = 1 } else { m[row][c] = 0 } bitIdx++ } } if dirUp { row-- } else { row++ } if row < 0 || row >= Size { break } } dirUp = !dirUp if row < 0 { row = 0 } else { row = Size - 1 } col -= 2 } } func applyMask(m [][]int) { for r := 0; r < Size; r++ { for c := 0; c < Size; c++ { if isFunctionModule(r, c) { continue } if (r+c)%2 == 0 { // flip if m[r][c] == 0 { m[r][c] = 1 } else if m[r][c] == 1 { m[r][c] = 0 } } } } } func isFunctionModule(r, c int) bool { if (r >= 0 && r <= 8 && c >= 0 && c <= 8) || (r >= 0 && r <= 8 && c >= Size-9 && c <= Size-1) || (r >= Size-9 && r <= Size-1 && c >= 0 && c <= 8) { return true } if r == 6 || c == 6 { return true } formatPos := [][2]int{ {8, 0}, {8, 1}, {8, 2}, {8, 3}, {8, 4}, {8, 5}, {8, 7}, {8, 8}, {7, 8}, {5, 8}, {4, 8}, {3, 8}, {2, 8}, {1, 8}, {0, 8}, {Size - 1, 8}, {Size - 2, 8}, {Size - 3, 8}, {Size - 4, 8}, {Size - 5, 8}, {8, Size - 1}, {8, Size - 2}, {8, Size - 3}, {8, Size - 4}, {8, Size - 5}, } for _, p := range formatPos { if p[0] == r && p[1] == c { return true } } return false } var formatBits15 = []int{ 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, } func placeFormatBits(m [][]int) { _ = [][2]int{{8, 0}, {8, 1}, {8, 2}, {8, 3}, {8, 4}, {8, 5}, {8, 7}, {8, 8}} _ = [][2]int{{7, 8}, {6, 8}, {5, 8}, {4, 8}, {3, 8}, {2, 8}, {1, 8}, {0, 8}} _ = [][2]int{{Size - 1, 8}, {Size - 2, 8}, {Size - 3, 8}, {Size - 4, 8}, {Size - 5, 8}, {Size - 6, 8}, {Size - 7, 8}, {Size - 8, 8}} _ = [][2]int{{8, Size - 1}, {8, Size - 2}, {8, Size - 3}, {8, Size - 4}, {8, Size - 5}, {8, Size - 6}, {8, Size - 7}, {8, Size - 8}} pos := [][2]int{ {8, 0}, {8, 1}, {8, 2}, {8, 3}, {8, 4}, {8, 5}, {8, 7}, {8, 8}, {7, 8}, {6, 8}, {5, 8}, {4, 8}, {3, 8}, {2, 8}, {1, 8}, } for i := 0; i < 15; i++ { r := pos[i][0] c := pos[i][1] m[r][c] = formatBits15[i] } m[Size-1][8] = formatBits15[0] m[Size-2][8] = formatBits15[1] m[Size-3][8] = formatBits15[2] m[Size-4][8] = formatBits15[3] m[Size-5][8] = formatBits15[4] m[Size-6][8] = formatBits15[5] m[Size-7][8] = formatBits15[6] m[8][Size-1] = formatBits15[7] m[8][Size-2] = formatBits15[8] m[8][Size-3] = formatBits15[9] m[8][Size-4] = formatBits15[10] m[8][Size-5] = formatBits15[11] m[8][Size-6] = formatBits15[12] m[8][Size-7] = formatBits15[13] m[8][Size-8] = formatBits15[14] } func bitsFromCodewords(codewords []int, ecc []int) []bool { total := append(codewords, ecc...) bits := make([]bool, 0, len(total)*8) for _, cw := range total { for i := 7; i >= 0; i-- { bits = append(bits, ((cw>>i)&1) == 1) } } return bits } func renderPNG(m [][]int, scale int, filename string) error { img := image.NewRGBA(image.Rect(0, 0, Size*scale, Size*scale)) white := color.RGBA{255, 255, 255, 255} black := color.RGBA{0, 0, 0, 255} for y := 0; y < Size*scale; y++ { for x := 0; x < Size*scale; x++ { img.Set(x, y, white) } } for r := 0; r < Size; r++ { for c := 0; c < Size; c++ { val := m[r][c] if val == 1 { // draw black for y := r * scale; y < (r+1)*scale; y++ { for x := c * scale; x < (c+1)*scale; x++ { img.Set(x, y, black) } } } } } f, err := os.Create(filename) if err != nil { return err } defer f.Close() return png.Encode(f, img) } func main() { initGFTables() payload := []byte("Hello from Ashok") dataCW, err := encodeBytesToCodewords(payload) if err != nil { fmt.Println("encode error:", err) return } gen := rsGenerator(ECCCodewords) ecc := rsComputeECC(dataCW, ECCCodewords, gen) mat := newMatrix() placeFinder(mat, 0, 0) placeFinder(mat, 0, Size-7) placeFinder(mat, Size-7, 0) placeTiming(mat) mat[8][Size-8] = 1 dataBits := bitsFromCodewords(dataCW, ecc) placeData(mat, dataBits) applyMask(mat) placeFormatBits(mat) out := "qrcode.png" if err := renderPNG(mat, 8, out); err != nil { fmt.Println("render error:", err) return } fmt.Println("Written", out) }