1package qr
2
3import (
4	"errors"
5	"image"
6	"image/color"
7	"github.com/vitrun/qart/coding"
8)
9
10// A Level denotes a QR error correction level.
11// From least to most tolerant of errors, they are L, M, Q, H.
12type Level int
13
14const (
15	L Level = iota // 20% redundant
16	M              // 38% redundant
17	Q              // 55% redundant
18	H              // 65% redundant
19)
20
21// Encode returns an encoding of text at the given error correction level.
22func Encode(text string, level Level) (*Code, error) {
23	// Pick data encoding, smallest first.
24	// We could split the string and use different encodings
25	// but that seems like overkill for now.
26	var enc coding.Encoding
27	switch {
28	case coding.Num(text).Check() == nil:
29		enc = coding.Num(text)
30	case coding.Alpha(text).Check() == nil:
31		enc = coding.Alpha(text)
32	default:
33		enc = coding.String(text)
34	}
35
36	// Pick size.
37	l := coding.Level(level)
38	var v coding.Version
39	for v = coding.MinVersion; ; v++ {
40		if v > coding.MaxVersion {
41			return nil, errors.New("text too long to encode as QR")
42		}
43		if enc.Bits(v) <= v.DataBytes(l)*8 {
44			break
45		}
46	}
47
48	// Build and execute plan.
49	p, err := coding.NewPlan(v, l, 0)
50	if err != nil {
51		return nil, err
52	}
53	cc, err := p.Encode(enc)
54	if err != nil {
55		return nil, err
56	}
57
58	// TODO: Pick appropriate mask.
59
60	return &Code{cc.Bitmap, cc.Size, cc.Stride, 8}, nil
61}
62
63// A Code is a square pixel grid.
64// It implements image.Image and direct PNG encoding.
65type Code struct {
66	Bitmap []byte // 1 is black, 0 is white
67	Size   int    // number of pixels on a side
68	Stride int    // number of bytes per row
69	Scale  int    // number of image pixels per QR pixel
70}
71
72// Black returns true if the pixel at (x,y) is black.
73func (c *Code) Black(x, y int) bool {
74	return 0 <= x && x < c.Size && 0 <= y && y < c.Size &&
75			c.Bitmap[y*c.Stride+x/8]&(1<<uint(7-x&7)) != 0
76}
77
78// Image returns an Image displaying the code.
79func (c *Code) Image() image.Image {
80	return &codeImage{c}
81
82}
83
84// codeImage implements image.Image
85type codeImage struct {
86	*Code
87}
88
89var (
90	whiteColor color.Color = color.Gray{0xFF}
91	blackColor color.Color = color.Gray{0x00}
92)
93
94func (c *codeImage) Bounds() image.Rectangle {
95	d := (c.Size + 8) * c.Scale
96	return image.Rect(0, 0, d, d)
97}
98
99func (c *codeImage) At(x, y int) color.Color {
100	if c.Black(x, y) {
101		return blackColor
102	}
103	return whiteColor
104}
105
106func (c *codeImage) ColorModel() color.Model {
107	return color.GrayModel
108}
109
110