summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattermost/rsc/qr/web
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattermost/rsc/qr/web')
-rw-r--r--vendor/github.com/mattermost/rsc/qr/web/pic.go506
-rw-r--r--vendor/github.com/mattermost/rsc/qr/web/play.go1118
-rw-r--r--vendor/github.com/mattermost/rsc/qr/web/resize/resize.go152
3 files changed, 1776 insertions, 0 deletions
diff --git a/vendor/github.com/mattermost/rsc/qr/web/pic.go b/vendor/github.com/mattermost/rsc/qr/web/pic.go
new file mode 100644
index 000000000..6baef94d2
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/qr/web/pic.go
@@ -0,0 +1,506 @@
+package web
+
+import (
+ "bytes"
+ "fmt"
+ "image"
+ "image/color"
+ "image/draw"
+ "image/png"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "code.google.com/p/freetype-go/freetype"
+ "github.com/mattermost/rsc/appfs/fs"
+ "github.com/mattermost/rsc/qr"
+ "github.com/mattermost/rsc/qr/coding"
+)
+
+func makeImage(req *http.Request, caption, font string, pt, size, border, scale int, f func(x, y int) uint32) *image.RGBA {
+ d := (size + 2*border) * scale
+ csize := 0
+ if caption != "" {
+ if pt == 0 {
+ pt = 11
+ }
+ csize = pt * 2
+ }
+ c := image.NewRGBA(image.Rect(0, 0, d, d+csize))
+
+ // white
+ u := &image.Uniform{C: color.White}
+ draw.Draw(c, c.Bounds(), u, image.ZP, draw.Src)
+
+ for y := 0; y < size; y++ {
+ for x := 0; x < size; x++ {
+ r := image.Rect((x+border)*scale, (y+border)*scale, (x+border+1)*scale, (y+border+1)*scale)
+ rgba := f(x, y)
+ u.C = color.RGBA{byte(rgba >> 24), byte(rgba >> 16), byte(rgba >> 8), byte(rgba)}
+ draw.Draw(c, r, u, image.ZP, draw.Src)
+ }
+ }
+
+ if csize != 0 {
+ if font == "" {
+ font = "data/luxisr.ttf"
+ }
+ ctxt := fs.NewContext(req)
+ dat, _, err := ctxt.Read(font)
+ if err != nil {
+ panic(err)
+ }
+ tfont, err := freetype.ParseFont(dat)
+ if err != nil {
+ panic(err)
+ }
+ ft := freetype.NewContext()
+ ft.SetDst(c)
+ ft.SetDPI(100)
+ ft.SetFont(tfont)
+ ft.SetFontSize(float64(pt))
+ ft.SetSrc(image.NewUniform(color.Black))
+ ft.SetClip(image.Rect(0, 0, 0, 0))
+ wid, err := ft.DrawString(caption, freetype.Pt(0, 0))
+ if err != nil {
+ panic(err)
+ }
+ p := freetype.Pt(d, d+3*pt/2)
+ p.X -= wid.X
+ p.X /= 2
+ ft.SetClip(c.Bounds())
+ ft.DrawString(caption, p)
+ }
+
+ return c
+}
+
+func makeFrame(req *http.Request, font string, pt, vers, l, scale, dots int) image.Image {
+ lev := coding.Level(l)
+ p, err := coding.NewPlan(coding.Version(vers), lev, 0)
+ if err != nil {
+ panic(err)
+ }
+
+ nd := p.DataBytes / p.Blocks
+ nc := p.CheckBytes / p.Blocks
+ extra := p.DataBytes - nd*p.Blocks
+
+ cap := fmt.Sprintf("QR v%d, %s", vers, lev)
+ if dots > 0 {
+ cap = fmt.Sprintf("QR v%d order, from bottom right", vers)
+ }
+ m := makeImage(req, cap, font, pt, len(p.Pixel), 0, scale, func(x, y int) uint32 {
+ pix := p.Pixel[y][x]
+ switch pix.Role() {
+ case coding.Data:
+ if dots > 0 {
+ return 0xffffffff
+ }
+ off := int(pix.Offset() / 8)
+ nd := nd
+ var i int
+ for i = 0; i < p.Blocks; i++ {
+ if i == extra {
+ nd++
+ }
+ if off < nd {
+ break
+ }
+ off -= nd
+ }
+ return blockColors[i%len(blockColors)]
+ case coding.Check:
+ if dots > 0 {
+ return 0xffffffff
+ }
+ i := (int(pix.Offset()/8) - p.DataBytes) / nc
+ return dark(blockColors[i%len(blockColors)])
+ }
+ if pix&coding.Black != 0 {
+ return 0x000000ff
+ }
+ return 0xffffffff
+ })
+
+ if dots > 0 {
+ b := m.Bounds()
+ for y := 0; y <= len(p.Pixel); y++ {
+ for x := 0; x < b.Dx(); x++ {
+ m.SetRGBA(x, y*scale-(y/len(p.Pixel)), color.RGBA{127, 127, 127, 255})
+ }
+ }
+ for x := 0; x <= len(p.Pixel); x++ {
+ for y := 0; y < b.Dx(); y++ {
+ m.SetRGBA(x*scale-(x/len(p.Pixel)), y, color.RGBA{127, 127, 127, 255})
+ }
+ }
+ order := make([]image.Point, (p.DataBytes+p.CheckBytes)*8+1)
+ for y, row := range p.Pixel {
+ for x, pix := range row {
+ if r := pix.Role(); r != coding.Data && r != coding.Check {
+ continue
+ }
+ // draw.Draw(m, m.Bounds().Add(image.Pt(x*scale, y*scale)), dot, image.ZP, draw.Over)
+ order[pix.Offset()] = image.Point{x*scale + scale/2, y*scale + scale/2}
+ }
+ }
+
+ for mode := 0; mode < 2; mode++ {
+ for i, p := range order {
+ q := order[i+1]
+ if q.X == 0 {
+ break
+ }
+ line(m, p, q, mode)
+ }
+ }
+ }
+ return m
+}
+
+func line(m *image.RGBA, p, q image.Point, mode int) {
+ x := 0
+ y := 0
+ dx := q.X - p.X
+ dy := q.Y - p.Y
+ xsign := +1
+ ysign := +1
+ if dx < 0 {
+ xsign = -1
+ dx = -dx
+ }
+ if dy < 0 {
+ ysign = -1
+ dy = -dy
+ }
+ pt := func() {
+ switch mode {
+ case 0:
+ for dx := -2; dx <= 2; dx++ {
+ for dy := -2; dy <= 2; dy++ {
+ if dy*dx <= -4 || dy*dx >= 4 {
+ continue
+ }
+ m.SetRGBA(p.X+x*xsign+dx, p.Y+y*ysign+dy, color.RGBA{255, 192, 192, 255})
+ }
+ }
+
+ case 1:
+ m.SetRGBA(p.X+x*xsign, p.Y+y*ysign, color.RGBA{128, 0, 0, 255})
+ }
+ }
+ if dx > dy {
+ for x < dx || y < dy {
+ pt()
+ x++
+ if float64(x)*float64(dy)/float64(dx)-float64(y) > 0.5 {
+ y++
+ }
+ }
+ } else {
+ for x < dx || y < dy {
+ pt()
+ y++
+ if float64(y)*float64(dx)/float64(dy)-float64(x) > 0.5 {
+ x++
+ }
+ }
+ }
+ pt()
+}
+
+func pngEncode(c image.Image) []byte {
+ var b bytes.Buffer
+ png.Encode(&b, c)
+ return b.Bytes()
+}
+
+// Frame handles a request for a single QR frame.
+func Frame(w http.ResponseWriter, req *http.Request) {
+ arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+ v := arg("v")
+ scale := arg("scale")
+ if scale == 0 {
+ scale = 8
+ }
+
+ w.Header().Set("Cache-Control", "public, max-age=3600")
+ w.Write(pngEncode(makeFrame(req, req.FormValue("font"), arg("pt"), v, arg("l"), scale, arg("dots"))))
+}
+
+// Frames handles a request for multiple QR frames.
+func Frames(w http.ResponseWriter, req *http.Request) {
+ vs := strings.Split(req.FormValue("v"), ",")
+
+ arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+ scale := arg("scale")
+ if scale == 0 {
+ scale = 8
+ }
+ font := req.FormValue("font")
+ pt := arg("pt")
+ dots := arg("dots")
+
+ var images []image.Image
+ l := arg("l")
+ for _, v := range vs {
+ l := l
+ if i := strings.Index(v, "."); i >= 0 {
+ l, _ = strconv.Atoi(v[i+1:])
+ v = v[:i]
+ }
+ vv, _ := strconv.Atoi(v)
+ images = append(images, makeFrame(req, font, pt, vv, l, scale, dots))
+ }
+
+ b := images[len(images)-1].Bounds()
+
+ dx := arg("dx")
+ if dx == 0 {
+ dx = b.Dx()
+ }
+ x, y := 0, 0
+ xmax := 0
+ sep := arg("sep")
+ if sep == 0 {
+ sep = 10
+ }
+ var points []image.Point
+ for i, m := range images {
+ if x > 0 {
+ x += sep
+ }
+ if x > 0 && x+m.Bounds().Dx() > dx {
+ y += sep + images[i-1].Bounds().Dy()
+ x = 0
+ }
+ points = append(points, image.Point{x, y})
+ x += m.Bounds().Dx()
+ if x > xmax {
+ xmax = x
+ }
+
+ }
+
+ c := image.NewRGBA(image.Rect(0, 0, xmax, y+b.Dy()))
+ for i, m := range images {
+ draw.Draw(c, c.Bounds().Add(points[i]), m, image.ZP, draw.Src)
+ }
+
+ w.Header().Set("Cache-Control", "public, max-age=3600")
+ w.Write(pngEncode(c))
+}
+
+// Mask handles a request for a single QR mask.
+func Mask(w http.ResponseWriter, req *http.Request) {
+ arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+ v := arg("v")
+ m := arg("m")
+ scale := arg("scale")
+ if scale == 0 {
+ scale = 8
+ }
+
+ w.Header().Set("Cache-Control", "public, max-age=3600")
+ w.Write(pngEncode(makeMask(req, req.FormValue("font"), arg("pt"), v, m, scale)))
+}
+
+// Masks handles a request for multiple QR masks.
+func Masks(w http.ResponseWriter, req *http.Request) {
+ arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+ v := arg("v")
+ scale := arg("scale")
+ if scale == 0 {
+ scale = 8
+ }
+ font := req.FormValue("font")
+ pt := arg("pt")
+ var mm []image.Image
+ for m := 0; m < 8; m++ {
+ mm = append(mm, makeMask(req, font, pt, v, m, scale))
+ }
+ dx := mm[0].Bounds().Dx()
+ dy := mm[0].Bounds().Dy()
+
+ sep := arg("sep")
+ if sep == 0 {
+ sep = 10
+ }
+ c := image.NewRGBA(image.Rect(0, 0, (dx+sep)*4-sep, (dy+sep)*2-sep))
+ for m := 0; m < 8; m++ {
+ x := (m % 4) * (dx + sep)
+ y := (m / 4) * (dy + sep)
+ draw.Draw(c, c.Bounds().Add(image.Pt(x, y)), mm[m], image.ZP, draw.Src)
+ }
+
+ w.Header().Set("Cache-Control", "public, max-age=3600")
+ w.Write(pngEncode(c))
+}
+
+var maskName = []string{
+ "(x+y) % 2",
+ "y % 2",
+ "x % 3",
+ "(x+y) % 3",
+ "(y/2 + x/3) % 2",
+ "xy%2 + xy%3",
+ "(xy%2 + xy%3) % 2",
+ "(xy%3 + (x+y)%2) % 2",
+}
+
+func makeMask(req *http.Request, font string, pt int, vers, mask, scale int) image.Image {
+ p, err := coding.NewPlan(coding.Version(vers), coding.L, coding.Mask(mask))
+ if err != nil {
+ panic(err)
+ }
+ m := makeImage(req, maskName[mask], font, pt, len(p.Pixel), 0, scale, func(x, y int) uint32 {
+ pix := p.Pixel[y][x]
+ switch pix.Role() {
+ case coding.Data, coding.Check:
+ if pix&coding.Invert != 0 {
+ return 0x000000ff
+ }
+ }
+ return 0xffffffff
+ })
+ return m
+}
+
+var blockColors = []uint32{
+ 0x7777ffff,
+ 0xffff77ff,
+ 0xff7777ff,
+ 0x77ffffff,
+ 0x1e90ffff,
+ 0xffffe0ff,
+ 0x8b6969ff,
+ 0x77ff77ff,
+ 0x9b30ffff,
+ 0x00bfffff,
+ 0x90e890ff,
+ 0xfff68fff,
+ 0xffec8bff,
+ 0xffa07aff,
+ 0xffa54fff,
+ 0xeee8aaff,
+ 0x98fb98ff,
+ 0xbfbfbfff,
+ 0x54ff9fff,
+ 0xffaeb9ff,
+ 0xb23aeeff,
+ 0xbbffffff,
+ 0x7fffd4ff,
+ 0xff7a7aff,
+ 0x00007fff,
+}
+
+func dark(x uint32) uint32 {
+ r, g, b, a := byte(x>>24), byte(x>>16), byte(x>>8), byte(x)
+ r = r/2 + r/4
+ g = g/2 + g/4
+ b = b/2 + b/4
+ return uint32(r)<<24 | uint32(g)<<16 | uint32(b)<<8 | uint32(a)
+}
+
+func clamp(x int) byte {
+ if x < 0 {
+ return 0
+ }
+ if x > 255 {
+ return 255
+ }
+ return byte(x)
+}
+
+func max(x, y int) int {
+ if x > y {
+ return x
+ }
+ return y
+}
+
+// Arrow handles a request for an arrow pointing in a given direction.
+func Arrow(w http.ResponseWriter, req *http.Request) {
+ arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+ dir := arg("dir")
+ size := arg("size")
+ if size == 0 {
+ size = 50
+ }
+ del := size / 10
+
+ m := image.NewRGBA(image.Rect(0, 0, size, size))
+
+ if dir == 4 {
+ draw.Draw(m, m.Bounds(), image.Black, image.ZP, draw.Src)
+ draw.Draw(m, image.Rect(5, 5, size-5, size-5), image.White, image.ZP, draw.Src)
+ }
+
+ pt := func(x, y int, c color.RGBA) {
+ switch dir {
+ case 0:
+ m.SetRGBA(x, y, c)
+ case 1:
+ m.SetRGBA(y, size-1-x, c)
+ case 2:
+ m.SetRGBA(size-1-x, size-1-y, c)
+ case 3:
+ m.SetRGBA(size-1-y, x, c)
+ }
+ }
+
+ for y := 0; y < size/2; y++ {
+ for x := 0; x < del && x < y; x++ {
+ pt(x, y, color.RGBA{0, 0, 0, 255})
+ }
+ for x := del; x < y-del; x++ {
+ pt(x, y, color.RGBA{128, 128, 255, 255})
+ }
+ for x := max(y-del, 0); x <= y; x++ {
+ pt(x, y, color.RGBA{0, 0, 0, 255})
+ }
+ }
+ for y := size / 2; y < size; y++ {
+ for x := 0; x < del && x < size-1-y; x++ {
+ pt(x, y, color.RGBA{0, 0, 0, 255})
+ }
+ for x := del; x < size-1-y-del; x++ {
+ pt(x, y, color.RGBA{128, 128, 192, 255})
+ }
+ for x := max(size-1-y-del, 0); x <= size-1-y; x++ {
+ pt(x, y, color.RGBA{0, 0, 0, 255})
+ }
+ }
+
+ w.Header().Set("Cache-Control", "public, max-age=3600")
+ w.Write(pngEncode(m))
+}
+
+// Encode encodes a string using the given version, level, and mask.
+func Encode(w http.ResponseWriter, req *http.Request) {
+ val := func(s string) int {
+ v, _ := strconv.Atoi(req.FormValue(s))
+ return v
+ }
+
+ l := coding.Level(val("l"))
+ v := coding.Version(val("v"))
+ enc := coding.String(req.FormValue("t"))
+ m := coding.Mask(val("m"))
+
+ p, err := coding.NewPlan(v, l, m)
+ if err != nil {
+ panic(err)
+ }
+ cc, err := p.Encode(enc)
+ if err != nil {
+ panic(err)
+ }
+
+ c := &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: 8}
+ w.Header().Set("Content-Type", "image/png")
+ w.Header().Set("Cache-Control", "public, max-age=3600")
+ w.Write(c.PNG())
+}
+
diff --git a/vendor/github.com/mattermost/rsc/qr/web/play.go b/vendor/github.com/mattermost/rsc/qr/web/play.go
new file mode 100644
index 000000000..120f50b81
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/qr/web/play.go
@@ -0,0 +1,1118 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+QR data layout
+
+qr/
+ upload/
+ id.png
+ id.fix
+ flag/
+ id
+
+*/
+// TODO: Random seed taken from GET for caching, repeatability.
+// TODO: Flag for abuse button + some kind of dashboard.
+// TODO: +1 button on web page? permalink?
+// TODO: Flag for abuse button on permalinks too?
+// TODO: Make the page prettier.
+// TODO: Cache headers.
+
+package web
+
+import (
+ "bytes"
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "image"
+ "image/color"
+ _ "image/gif"
+ _ "image/jpeg"
+ "image/png"
+ "io"
+ "math/rand"
+ "net/http"
+ "net/url"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/mattermost/rsc/appfs/fs"
+ "github.com/mattermost/rsc/gf256"
+ "github.com/mattermost/rsc/qr"
+ "github.com/mattermost/rsc/qr/coding"
+ "github.com/mattermost/rsc/qr/web/resize"
+)
+
+func runTemplate(c *fs.Context, w http.ResponseWriter, name string, data interface{}) {
+ t := template.New("main")
+
+ main, _, err := c.Read(name)
+ if err != nil {
+ panic(err)
+ }
+ style, _, _ := c.Read("style.html")
+ main = append(main, style...)
+ _, err = t.Parse(string(main))
+ if err != nil {
+ panic(err)
+ }
+
+ var buf bytes.Buffer
+ if err := t.Execute(&buf, &data); err != nil {
+ panic(err)
+ }
+ w.Write(buf.Bytes())
+}
+
+func isImgName(s string) bool {
+ if len(s) != 32 {
+ return false
+ }
+ for i := 0; i < len(s); i++ {
+ if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' {
+ continue
+ }
+ return false
+ }
+ return true
+}
+
+func isTagName(s string) bool {
+ if len(s) != 16 {
+ return false
+ }
+ for i := 0; i < len(s); i++ {
+ if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' {
+ continue
+ }
+ return false
+ }
+ return true
+}
+
+// Draw is the handler for drawing a QR code.
+func Draw(w http.ResponseWriter, req *http.Request) {
+ ctxt := fs.NewContext(req)
+
+ url := req.FormValue("url")
+ if url == "" {
+ url = "http://swtch.com/qr"
+ }
+ if req.FormValue("upload") == "1" {
+ upload(w, req, url)
+ return
+ }
+
+ t0 := time.Now()
+ img := req.FormValue("i")
+ if !isImgName(img) {
+ img = "pjw"
+ }
+ if req.FormValue("show") == "png" {
+ i := loadSize(ctxt, img, 48)
+ var buf bytes.Buffer
+ png.Encode(&buf, i)
+ w.Write(buf.Bytes())
+ return
+ }
+ if req.FormValue("flag") == "1" {
+ flag(w, req, img, ctxt)
+ return
+ }
+ if req.FormValue("x") == "" {
+ var data = struct {
+ Name string
+ URL string
+ }{
+ Name: img,
+ URL: url,
+ }
+ runTemplate(ctxt, w, "qr/main.html", &data)
+ return
+ }
+
+ arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+ targ := makeTarg(ctxt, img, 17+4*arg("v")+arg("z"))
+
+ m := &Image{
+ Name: img,
+ Dx: arg("x"),
+ Dy: arg("y"),
+ URL: req.FormValue("u"),
+ Version: arg("v"),
+ Mask: arg("m"),
+ RandControl: arg("r") > 0,
+ Dither: arg("i") > 0,
+ OnlyDataBits: arg("d") > 0,
+ SaveControl: arg("c") > 0,
+ Scale: arg("scale"),
+ Target: targ,
+ Seed: int64(arg("s")),
+ Rotation: arg("o"),
+ Size: arg("z"),
+ }
+ if m.Version > 8 {
+ m.Version = 8
+ }
+
+ if m.Scale == 0 {
+ if arg("l") > 1 {
+ m.Scale = 8
+ } else {
+ m.Scale = 4
+ }
+ }
+ if m.Version >= 12 && m.Scale >= 4 {
+ m.Scale /= 2
+ }
+
+ if arg("l") == 1 {
+ data, err := json.Marshal(m)
+ if err != nil {
+ panic(err)
+ }
+ h := md5.New()
+ h.Write(data)
+ tag := fmt.Sprintf("%x", h.Sum(nil))[:16]
+ if err := ctxt.Write("qrsave/"+tag, data); err != nil {
+ panic(err)
+ }
+ http.Redirect(w, req, "/qr/show/" + tag, http.StatusTemporaryRedirect)
+ return
+ }
+
+ if err := m.Encode(req); err != nil {
+ fmt.Fprintf(w, "%s\n", err)
+ return
+ }
+
+ var dat []byte
+ switch {
+ case m.SaveControl:
+ dat = m.Control
+ default:
+ dat = m.Code.PNG()
+ }
+
+ if arg("l") > 0 {
+ w.Header().Set("Content-Type", "image/png")
+ w.Write(dat)
+ return
+ }
+
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ fmt.Fprint(w, "<center><img src=\"data:image/png;base64,")
+ io.WriteString(w, base64.StdEncoding.EncodeToString(dat))
+ fmt.Fprint(w, "\" /><br>")
+ fmt.Fprintf(w, "<form method=\"POST\" action=\"%s&l=1\"><input type=\"submit\" value=\"Save this QR code\"></form>\n", m.Link())
+ fmt.Fprintf(w, "</center>\n")
+ fmt.Fprintf(w, "<br><center><font size=-1>%v</font></center>\n", time.Now().Sub(t0))
+}
+
+func (m *Image) Small() bool {
+ return 8*(17+4*int(m.Version)) < 512
+}
+
+func (m *Image) Link() string {
+ s := fmt.Sprint
+ b := func(v bool) string {
+ if v {
+ return "1"
+ }
+ return "0"
+ }
+ val := url.Values{
+ "i": {m.Name},
+ "x": {s(m.Dx)},
+ "y": {s(m.Dy)},
+ "z": {s(m.Size)},
+ "u": {m.URL},
+ "v": {s(m.Version)},
+ "m": {s(m.Mask)},
+ "r": {b(m.RandControl)},
+ "t": {b(m.Dither)},
+ "d": {b(m.OnlyDataBits)},
+ "c": {b(m.SaveControl)},
+ "s": {s(m.Seed)},
+ }
+ return "/qr/draw?" + val.Encode()
+}
+
+// Show is the handler for showing a stored QR code.
+func Show(w http.ResponseWriter, req *http.Request) {
+ ctxt := fs.NewContext(req)
+ tag := req.URL.Path[len("/qr/show/"):]
+ png := strings.HasSuffix(tag, ".png")
+ if png {
+ tag = tag[:len(tag)-len(".png")]
+ }
+ if !isTagName(tag) {
+ fmt.Fprintf(w, "Sorry, QR code not found\n")
+ return
+ }
+ if req.FormValue("flag") == "1" {
+ flag(w, req, tag, ctxt)
+ return
+ }
+ data, _, err := ctxt.Read("qrsave/" + tag)
+ if err != nil {
+ fmt.Fprintf(w, "Sorry, QR code not found.\n")
+ return
+ }
+
+ var m Image
+ if err := json.Unmarshal(data, &m); err != nil {
+ panic(err)
+ }
+ m.Tag = tag
+
+ switch req.FormValue("size") {
+ case "big":
+ m.Scale *= 2
+ case "small":
+ m.Scale /= 2
+ }
+
+ if png {
+ if err := m.Encode(req); err != nil {
+ panic(err)
+ return
+ }
+ w.Header().Set("Cache-Control", "public, max-age=3600")
+ w.Write(m.Code.PNG())
+ return
+ }
+
+ w.Header().Set("Cache-Control", "public, max-age=300")
+ runTemplate(ctxt, w, "qr/permalink.html", &m)
+}
+
+func upload(w http.ResponseWriter, req *http.Request, link string) {
+ // Upload of a new image.
+ // Copied from Moustachio demo.
+ f, _, err := req.FormFile("image")
+ if err != nil {
+ fmt.Fprintf(w, "You need to select an image to upload.\n")
+ return
+ }
+ defer f.Close()
+
+ i, _, err := image.Decode(f)
+ if err != nil {
+ panic(err)
+ }
+
+ // Convert image to 128x128 gray+alpha.
+ b := i.Bounds()
+ const max = 128
+ // If it's gigantic, it's more efficient to downsample first
+ // and then resize; resizing will smooth out the roughness.
+ var i1 *image.RGBA
+ if b.Dx() > 4*max || b.Dy() > 4*max {
+ w, h := 2*max, 2*max
+ if b.Dx() > b.Dy() {
+ h = b.Dy() * h / b.Dx()
+ } else {
+ w = b.Dx() * w / b.Dy()
+ }
+ i1 = resize.Resample(i, b, w, h)
+ } else {
+ // "Resample" to same size, just to convert to RGBA.
+ i1 = resize.Resample(i, b, b.Dx(), b.Dy())
+ }
+ b = i1.Bounds()
+
+ // Encode to PNG.
+ dx, dy := 128, 128
+ if b.Dx() > b.Dy() {
+ dy = b.Dy() * dx / b.Dx()
+ } else {
+ dx = b.Dx() * dy / b.Dy()
+ }
+ i128 := resize.ResizeRGBA(i1, i1.Bounds(), dx, dy)
+
+ var buf bytes.Buffer
+ if err := png.Encode(&buf, i128); err != nil {
+ panic(err)
+ }
+
+ h := md5.New()
+ h.Write(buf.Bytes())
+ tag := fmt.Sprintf("%x", h.Sum(nil))[:32]
+
+ ctxt := fs.NewContext(req)
+ if err := ctxt.Write("qr/upload/"+tag+".png", buf.Bytes()); err != nil {
+ panic(err)
+ }
+
+ // Redirect with new image tag.
+ // Redirect to draw with new image tag.
+ http.Redirect(w, req, req.URL.Path+"?"+url.Values{"i": {tag}, "url": {link}}.Encode(), 302)
+}
+
+func flag(w http.ResponseWriter, req *http.Request, img string, ctxt *fs.Context) {
+ if !isImgName(img) && !isTagName(img) {
+ fmt.Fprintf(w, "Invalid image.\n")
+ return
+ }
+ data, _, _ := ctxt.Read("qr/flag/" + img)
+ data = append(data, '!')
+ ctxt.Write("qr/flag/" + img, data)
+
+ fmt.Fprintf(w, "Thank you. The image has been reported.\n")
+}
+
+func loadSize(ctxt *fs.Context, name string, max int) *image.RGBA {
+ data, _, err := ctxt.Read("qr/upload/" + name + ".png")
+ if err != nil {
+ panic(err)
+ }
+ i, _, err := image.Decode(bytes.NewBuffer(data))
+ if err != nil {
+ panic(err)
+ }
+ b := i.Bounds()
+ dx, dy := max, max
+ if b.Dx() > b.Dy() {
+ dy = b.Dy() * dx / b.Dx()
+ } else {
+ dx = b.Dx() * dy / b.Dy()
+ }
+ var irgba *image.RGBA
+ switch i := i.(type) {
+ case *image.RGBA:
+ irgba = resize.ResizeRGBA(i, i.Bounds(), dx, dy)
+ case *image.NRGBA:
+ irgba = resize.ResizeNRGBA(i, i.Bounds(), dx, dy)
+ }
+ return irgba
+}
+
+func makeTarg(ctxt *fs.Context, name string, max int) [][]int {
+ i := loadSize(ctxt, name, max)
+ b := i.Bounds()
+ dx, dy := b.Dx(), b.Dy()
+ targ := make([][]int, dy)
+ arr := make([]int, dx*dy)
+ for y := 0; y < dy; y++ {
+ targ[y], arr = arr[:dx], arr[dx:]
+ row := targ[y]
+ for x := 0; x < dx; x++ {
+ p := i.Pix[y*i.Stride+4*x:]
+ r, g, b, a := p[0], p[1], p[2], p[3]
+ if a == 0 {
+ row[x] = -1
+ } else {
+ row[x] = int((299*uint32(r) + 587*uint32(g) + 114*uint32(b) + 500) / 1000)
+ }
+ }
+ }
+ return targ
+}
+
+type Image struct {
+ Name string
+ Target [][]int
+ Dx int
+ Dy int
+ URL string
+ Tag string
+ Version int
+ Mask int
+ Scale int
+ Rotation int
+ Size int
+
+ // RandControl says to pick the pixels randomly.
+ RandControl bool
+ Seed int64
+
+ // Dither says to dither instead of using threshold pixel layout.
+ Dither bool
+
+ // OnlyDataBits says to use only data bits, not check bits.
+ OnlyDataBits bool
+
+ // Code is the final QR code.
+ Code *qr.Code
+
+ // Control is a PNG showing the pixels that we controlled.
+ // Pixels we don't control are grayed out.
+ SaveControl bool
+ Control []byte
+}
+
+type Pixinfo struct {
+ X int
+ Y int
+ Pix coding.Pixel
+ Targ byte
+ DTarg int
+ Contrast int
+ HardZero bool
+ Block *BitBlock
+ Bit uint
+}
+
+type Pixorder struct {
+ Off int
+ Priority int
+}
+
+type byPriority []Pixorder
+
+func (x byPriority) Len() int { return len(x) }
+func (x byPriority) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byPriority) Less(i, j int) bool { return x[i].Priority > x[j].Priority }
+
+func (m *Image) target(x, y int) (targ byte, contrast int) {
+ tx := x + m.Dx
+ ty := y + m.Dy
+ if ty < 0 || ty >= len(m.Target) || tx < 0 || tx >= len(m.Target[ty]) {
+ return 255, -1
+ }
+
+ v0 := m.Target[ty][tx]
+ if v0 < 0 {
+ return 255, -1
+ }
+ targ = byte(v0)
+
+ n := 0
+ sum := 0
+ sumsq := 0
+ const del = 5
+ for dy := -del; dy <= del; dy++ {
+ for dx := -del; dx <= del; dx++ {
+ if 0 <= ty+dy && ty+dy < len(m.Target) && 0 <= tx+dx && tx+dx < len(m.Target[ty+dy]) {
+ v := m.Target[ty+dy][tx+dx]
+ sum += v
+ sumsq += v * v
+ n++
+ }
+ }
+ }
+
+ avg := sum / n
+ contrast = sumsq/n - avg*avg
+ return
+}
+
+func (m *Image) rotate(p *coding.Plan, rot int) {
+ if rot == 0 {
+ return
+ }
+
+ N := len(p.Pixel)
+ pix := make([][]coding.Pixel, N)
+ apix := make([]coding.Pixel, N*N)
+ for i := range pix {
+ pix[i], apix = apix[:N], apix[N:]
+ }
+
+ switch rot {
+ case 0:
+ // ok
+ case 1:
+ for y := 0; y < N; y++ {
+ for x := 0; x < N; x++ {
+ pix[y][x] = p.Pixel[x][N-1-y]
+ }
+ }
+ case 2:
+ for y := 0; y < N; y++ {
+ for x := 0; x < N; x++ {
+ pix[y][x] = p.Pixel[N-1-y][N-1-x]
+ }
+ }
+ case 3:
+ for y := 0; y < N; y++ {
+ for x := 0; x < N; x++ {
+ pix[y][x] = p.Pixel[N-1-x][y]
+ }
+ }
+ }
+
+ p.Pixel = pix
+}
+
+func (m *Image) Encode(req *http.Request) error {
+ p, err := coding.NewPlan(coding.Version(m.Version), coding.L, coding.Mask(m.Mask))
+ if err != nil {
+ return err
+ }
+
+ m.rotate(p, m.Rotation)
+
+ rand := rand.New(rand.NewSource(m.Seed))
+
+ // QR parameters.
+ nd := p.DataBytes / p.Blocks
+ nc := p.CheckBytes / p.Blocks
+ extra := p.DataBytes - nd*p.Blocks
+ rs := gf256.NewRSEncoder(coding.Field, nc)
+
+ // Build information about pixels, indexed by data/check bit number.
+ pixByOff := make([]Pixinfo, (p.DataBytes+p.CheckBytes)*8)
+ expect := make([][]bool, len(p.Pixel))
+ for y, row := range p.Pixel {
+ expect[y] = make([]bool, len(row))
+ for x, pix := range row {
+ targ, contrast := m.target(x, y)
+ if m.RandControl && contrast >= 0 {
+ contrast = rand.Intn(128) + 64*((x+y)%2) + 64*((x+y)%3%2)
+ }
+ expect[y][x] = pix&coding.Black != 0
+ if r := pix.Role(); r == coding.Data || r == coding.Check {
+ pixByOff[pix.Offset()] = Pixinfo{X: x, Y: y, Pix: pix, Targ: targ, Contrast: contrast}
+ }
+ }
+ }
+
+Again:
+ // Count fixed initial data bits, prepare template URL.
+ url := m.URL + "#"
+ var b coding.Bits
+ coding.String(url).Encode(&b, p.Version)
+ coding.Num("").Encode(&b, p.Version)
+ bbit := b.Bits()
+ dbit := p.DataBytes*8 - bbit
+ if dbit < 0 {
+ return fmt.Errorf("cannot encode URL into available bits")
+ }
+ num := make([]byte, dbit/10*3)
+ for i := range num {
+ num[i] = '0'
+ }
+ b.Pad(dbit)
+ b.Reset()
+ coding.String(url).Encode(&b, p.Version)
+ coding.Num(num).Encode(&b, p.Version)
+ b.AddCheckBytes(p.Version, p.Level)
+ data := b.Bytes()
+
+ doff := 0 // data offset
+ coff := 0 // checksum offset
+ mbit := bbit + dbit/10*10
+
+ // Choose pixels.
+ bitblocks := make([]*BitBlock, p.Blocks)
+ for blocknum := 0; blocknum < p.Blocks; blocknum++ {
+ if blocknum == p.Blocks-extra {
+ nd++
+ }
+
+ bdata := data[doff/8 : doff/8+nd]
+ cdata := data[p.DataBytes+coff/8 : p.DataBytes+coff/8+nc]
+ bb := newBlock(nd, nc, rs, bdata, cdata)
+ bitblocks[blocknum] = bb
+
+ // Determine which bits in this block we can try to edit.
+ lo, hi := 0, nd*8
+ if lo < bbit-doff {
+ lo = bbit - doff
+ if lo > hi {
+ lo = hi
+ }
+ }
+ if hi > mbit-doff {
+ hi = mbit - doff
+ if hi < lo {
+ hi = lo
+ }
+ }
+
+ // Preserve [0, lo) and [hi, nd*8).
+ for i := 0; i < lo; i++ {
+ if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) {
+ return fmt.Errorf("cannot preserve required bits")
+ }
+ }
+ for i := hi; i < nd*8; i++ {
+ if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) {
+ return fmt.Errorf("cannot preserve required bits")
+ }
+ }
+
+ // Can edit [lo, hi) and checksum bits to hit target.
+ // Determine which ones to try first.
+ order := make([]Pixorder, (hi-lo)+nc*8)
+ for i := lo; i < hi; i++ {
+ order[i-lo].Off = doff + i
+ }
+ for i := 0; i < nc*8; i++ {
+ order[hi-lo+i].Off = p.DataBytes*8 + coff + i
+ }
+ if m.OnlyDataBits {
+ order = order[:hi-lo]
+ }
+ for i := range order {
+ po := &order[i]
+ po.Priority = pixByOff[po.Off].Contrast<<8 | rand.Intn(256)
+ }
+ sort.Sort(byPriority(order))
+
+ const mark = false
+ for i := range order {
+ po := &order[i]
+ pinfo := &pixByOff[po.Off]
+ bval := pinfo.Targ
+ if bval < 128 {
+ bval = 1
+ } else {
+ bval = 0
+ }
+ pix := pinfo.Pix
+ if pix&coding.Invert != 0 {
+ bval ^= 1
+ }
+ if pinfo.HardZero {
+ bval = 0
+ }
+
+ var bi int
+ if pix.Role() == coding.Data {
+ bi = po.Off - doff
+ } else {
+ bi = po.Off - p.DataBytes*8 - coff + nd*8
+ }
+ if bb.canSet(uint(bi), bval) {
+ pinfo.Block = bb
+ pinfo.Bit = uint(bi)
+ if mark {
+ p.Pixel[pinfo.Y][pinfo.X] = coding.Black
+ }
+ } else {
+ if pinfo.HardZero {
+ panic("hard zero")
+ }
+ if mark {
+ p.Pixel[pinfo.Y][pinfo.X] = 0
+ }
+ }
+ }
+ bb.copyOut()
+
+ const cheat = false
+ for i := 0; i < nd*8; i++ {
+ pinfo := &pixByOff[doff+i]
+ pix := p.Pixel[pinfo.Y][pinfo.X]
+ if bb.B[i/8]&(1<<uint(7-i&7)) != 0 {
+ pix ^= coding.Black
+ }
+ expect[pinfo.Y][pinfo.X] = pix&coding.Black != 0
+ if cheat {
+ p.Pixel[pinfo.Y][pinfo.X] = pix & coding.Black
+ }
+ }
+ for i := 0; i < nc*8; i++ {
+ pinfo := &pixByOff[p.DataBytes*8+coff+i]
+ pix := p.Pixel[pinfo.Y][pinfo.X]
+ if bb.B[nd+i/8]&(1<<uint(7-i&7)) != 0 {
+ pix ^= coding.Black
+ }
+ expect[pinfo.Y][pinfo.X] = pix&coding.Black != 0
+ if cheat {
+ p.Pixel[pinfo.Y][pinfo.X] = pix & coding.Black
+ }
+ }
+ doff += nd * 8
+ coff += nc * 8
+ }
+
+ // Pass over all pixels again, dithering.
+ if m.Dither {
+ for i := range pixByOff {
+ pinfo := &pixByOff[i]
+ pinfo.DTarg = int(pinfo.Targ)
+ }
+ for y, row := range p.Pixel {
+ for x, pix := range row {
+ if pix.Role() != coding.Data && pix.Role() != coding.Check {
+ continue
+ }
+ pinfo := &pixByOff[pix.Offset()]
+ if pinfo.Block == nil {
+ // did not choose this pixel
+ continue
+ }
+
+ pix := pinfo.Pix
+
+ pval := byte(1) // pixel value (black)
+ v := 0 // gray value (black)
+ targ := pinfo.DTarg
+ if targ >= 128 {
+ // want white
+ pval = 0
+ v = 255
+ }
+
+ bval := pval // bit value
+ if pix&coding.Invert != 0 {
+ bval ^= 1
+ }
+ if pinfo.HardZero && bval != 0 {
+ bval ^= 1
+ pval ^= 1
+ v ^= 255
+ }
+
+ // Set pixel value as we want it.
+ pinfo.Block.reset(pinfo.Bit, bval)
+
+ _, _ = x, y
+
+ err := targ - v
+ if x+1 < len(row) {
+ addDither(pixByOff, row[x+1], err*7/16)
+ }
+ if false && y+1 < len(p.Pixel) {
+ if x > 0 {
+ addDither(pixByOff, p.Pixel[y+1][x-1], err*3/16)
+ }
+ addDither(pixByOff, p.Pixel[y+1][x], err*5/16)
+ if x+1 < len(row) {
+ addDither(pixByOff, p.Pixel[y+1][x+1], err*1/16)
+ }
+ }
+ }
+ }
+
+ for _, bb := range bitblocks {
+ bb.copyOut()
+ }
+ }
+
+ noops := 0
+ // Copy numbers back out.
+ for i := 0; i < dbit/10; i++ {
+ // Pull out 10 bits.
+ v := 0
+ for j := 0; j < 10; j++ {
+ bi := uint(bbit + 10*i + j)
+ v <<= 1
+ v |= int((data[bi/8] >> (7 - bi&7)) & 1)
+ }
+ // Turn into 3 digits.
+ if v >= 1000 {
+ // Oops - too many 1 bits.
+ // We know the 512, 256, 128, 64, 32 bits are all set.
+ // Pick one at random to clear. This will break some
+ // checksum bits, but so be it.
+ println("oops", i, v)
+ pinfo := &pixByOff[bbit+10*i+3] // TODO random
+ pinfo.Contrast = 1e9 >> 8
+ pinfo.HardZero = true
+ noops++
+ }
+ num[i*3+0] = byte(v/100 + '0')
+ num[i*3+1] = byte(v/10%10 + '0')
+ num[i*3+2] = byte(v%10 + '0')
+ }
+ if noops > 0 {
+ goto Again
+ }
+
+ var b1 coding.Bits
+ coding.String(url).Encode(&b1, p.Version)
+ coding.Num(num).Encode(&b1, p.Version)
+ b1.AddCheckBytes(p.Version, p.Level)
+ if !bytes.Equal(b.Bytes(), b1.Bytes()) {
+ fmt.Printf("mismatch\n%d %x\n%d %x\n", len(b.Bytes()), b.Bytes(), len(b1.Bytes()), b1.Bytes())
+ panic("byte mismatch")
+ }
+
+ cc, err := p.Encode(coding.String(url), coding.Num(num))
+ if err != nil {
+ return err
+ }
+
+ if !m.Dither {
+ for y, row := range expect {
+ for x, pix := range row {
+ if cc.Black(x, y) != pix {
+ println("mismatch", x, y, p.Pixel[y][x].String())
+ }
+ }
+ }
+ }
+
+ m.Code = &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: m.Scale}
+
+ if m.SaveControl {
+ m.Control = pngEncode(makeImage(req, "", "", 0, cc.Size, 4, m.Scale, func(x, y int) (rgba uint32) {
+ pix := p.Pixel[y][x]
+ if pix.Role() == coding.Data || pix.Role() == coding.Check {
+ pinfo := &pixByOff[pix.Offset()]
+ if pinfo.Block != nil {
+ if cc.Black(x, y) {
+ return 0x000000ff
+ }
+ return 0xffffffff
+ }
+ }
+ if cc.Black(x, y) {
+ return 0x3f3f3fff
+ }
+ return 0xbfbfbfff
+ }))
+ }
+
+ return nil
+}
+
+func addDither(pixByOff []Pixinfo, pix coding.Pixel, err int) {
+ if pix.Role() != coding.Data && pix.Role() != coding.Check {
+ return
+ }
+ pinfo := &pixByOff[pix.Offset()]
+ println("add", pinfo.X, pinfo.Y, pinfo.DTarg, err)
+ pinfo.DTarg += err
+}
+
+func readTarget(name string) ([][]int, error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ m, err := png.Decode(f)
+ if err != nil {
+ return nil, fmt.Errorf("decode %s: %v", name, err)
+ }
+ rect := m.Bounds()
+ target := make([][]int, rect.Dy())
+ for i := range target {
+ target[i] = make([]int, rect.Dx())
+ }
+ for y, row := range target {
+ for x := range row {
+ a := int(color.RGBAModel.Convert(m.At(x, y)).(color.RGBA).A)
+ t := int(color.GrayModel.Convert(m.At(x, y)).(color.Gray).Y)
+ if a == 0 {
+ t = -1
+ }
+ row[x] = t
+ }
+ }
+ return target, nil
+}
+
+type BitBlock struct {
+ DataBytes int
+ CheckBytes int
+ B []byte
+ M [][]byte
+ Tmp []byte
+ RS *gf256.RSEncoder
+ bdata []byte
+ cdata []byte
+}
+
+func newBlock(nd, nc int, rs *gf256.RSEncoder, dat, cdata []byte) *BitBlock {
+ b := &BitBlock{
+ DataBytes: nd,
+ CheckBytes: nc,
+ B: make([]byte, nd+nc),
+ Tmp: make([]byte, nc),
+ RS: rs,
+ bdata: dat,
+ cdata: cdata,
+ }
+ copy(b.B, dat)
+ rs.ECC(b.B[:nd], b.B[nd:])
+ b.check()
+ if !bytes.Equal(b.Tmp, cdata) {
+ panic("cdata")
+ }
+
+ b.M = make([][]byte, nd*8)
+ for i := range b.M {
+ row := make([]byte, nd+nc)
+ b.M[i] = row
+ for j := range row {
+ row[j] = 0
+ }
+ row[i/8] = 1 << (7 - uint(i%8))
+ rs.ECC(row[:nd], row[nd:])
+ }
+ return b
+}
+
+func (b *BitBlock) check() {
+ b.RS.ECC(b.B[:b.DataBytes], b.Tmp)
+ if !bytes.Equal(b.B[b.DataBytes:], b.Tmp) {
+ fmt.Printf("ecc mismatch\n%x\n%x\n", b.B[b.DataBytes:], b.Tmp)
+ panic("mismatch")
+ }
+}
+
+func (b *BitBlock) reset(bi uint, bval byte) {
+ if (b.B[bi/8]>>(7-bi&7))&1 == bval {
+ // already has desired bit
+ return
+ }
+ // rows that have already been set
+ m := b.M[len(b.M):cap(b.M)]
+ for _, row := range m {
+ if row[bi/8]&(1<<(7-bi&7)) != 0 {
+ // Found it.
+ for j, v := range row {
+ b.B[j] ^= v
+ }
+ return
+ }
+ }
+ panic("reset of unset bit")
+}
+
+func (b *BitBlock) canSet(bi uint, bval byte) bool {
+ found := false
+ m := b.M
+ for j, row := range m {
+ if row[bi/8]&(1<<(7-bi&7)) == 0 {
+ continue
+ }
+ if !found {
+ found = true
+ if j != 0 {
+ m[0], m[j] = m[j], m[0]
+ }
+ continue
+ }
+ for k := range row {
+ row[k] ^= m[0][k]
+ }
+ }
+ if !found {
+ return false
+ }
+
+ targ := m[0]
+
+ // Subtract from saved-away rows too.
+ for _, row := range m[len(m):cap(m)] {
+ if row[bi/8]&(1<<(7-bi&7)) == 0 {
+ continue
+ }
+ for k := range row {
+ row[k] ^= targ[k]
+ }
+ }
+
+ // Found a row with bit #bi == 1 and cut that bit from all the others.
+ // Apply to data and remove from m.
+ if (b.B[bi/8]>>(7-bi&7))&1 != bval {
+ for j, v := range targ {
+ b.B[j] ^= v
+ }
+ }
+ b.check()
+ n := len(m) - 1
+ m[0], m[n] = m[n], m[0]
+ b.M = m[:n]
+
+ for _, row := range b.M {
+ if row[bi/8]&(1<<(7-bi&7)) != 0 {
+ panic("did not reduce")
+ }
+ }
+
+ return true
+}
+
+func (b *BitBlock) copyOut() {
+ b.check()
+ copy(b.bdata, b.B[:b.DataBytes])
+ copy(b.cdata, b.B[b.DataBytes:])
+}
+
+func showtable(w http.ResponseWriter, b *BitBlock, gray func(int) bool) {
+ nd := b.DataBytes
+ nc := b.CheckBytes
+
+ fmt.Fprintf(w, "<table class='matrix' cellspacing=0 cellpadding=0 border=0>\n")
+ line := func() {
+ fmt.Fprintf(w, "<tr height=1 bgcolor='#bbbbbb'><td colspan=%d>\n", (nd+nc)*8)
+ }
+ line()
+ dorow := func(row []byte) {
+ fmt.Fprintf(w, "<tr>\n")
+ for i := 0; i < (nd+nc)*8; i++ {
+ fmt.Fprintf(w, "<td")
+ v := row[i/8] >> uint(7-i&7) & 1
+ if gray(i) {
+ fmt.Fprintf(w, " class='gray'")
+ }
+ fmt.Fprintf(w, ">")
+ if v == 1 {
+ fmt.Fprintf(w, "1")
+ }
+ }
+ line()
+ }
+
+ m := b.M[len(b.M):cap(b.M)]
+ for i := len(m) - 1; i >= 0; i-- {
+ dorow(m[i])
+ }
+ m = b.M
+ for _, row := range b.M {
+ dorow(row)
+ }
+
+ fmt.Fprintf(w, "</table>\n")
+}
+
+func BitsTable(w http.ResponseWriter, req *http.Request) {
+ nd := 2
+ nc := 2
+ fmt.Fprintf(w, `<html>
+ <style type='text/css'>
+ .matrix {
+ font-family: sans-serif;
+ font-size: 0.8em;
+ }
+ table.matrix {
+ padding-left: 1em;
+ padding-right: 1em;
+ padding-top: 1em;
+ padding-bottom: 1em;
+ }
+ .matrix td {
+ padding-left: 0.3em;
+ padding-right: 0.3em;
+ border-left: 2px solid white;
+ border-right: 2px solid white;
+ text-align: center;
+ color: #aaa;
+ }
+ .matrix td.gray {
+ color: black;
+ background-color: #ddd;
+ }
+ </style>
+ `)
+ rs := gf256.NewRSEncoder(coding.Field, nc)
+ dat := make([]byte, nd+nc)
+ b := newBlock(nd, nc, rs, dat[:nd], dat[nd:])
+ for i := 0; i < nd*8; i++ {
+ b.canSet(uint(i), 0)
+ }
+ showtable(w, b, func(i int) bool { return i < nd*8 })
+
+ b = newBlock(nd, nc, rs, dat[:nd], dat[nd:])
+ for j := 0; j < (nd+nc)*8; j += 2 {
+ b.canSet(uint(j), 0)
+ }
+ showtable(w, b, func(i int) bool { return i%2 == 0 })
+
+}
diff --git a/vendor/github.com/mattermost/rsc/qr/web/resize/resize.go b/vendor/github.com/mattermost/rsc/qr/web/resize/resize.go
new file mode 100644
index 000000000..02c8b0040
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/qr/web/resize/resize.go
@@ -0,0 +1,152 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package resize
+
+import (
+ "image"
+ "image/color"
+)
+
+// average convert the sums to averages and returns the result.
+func average(sum []uint64, w, h int, n uint64) *image.RGBA {
+ ret := image.NewRGBA(image.Rect(0, 0, w, h))
+ for y := 0; y < h; y++ {
+ for x := 0; x < w; x++ {
+ index := 4 * (y*w + x)
+ pix := ret.Pix[y*ret.Stride+x*4:]
+ pix[0] = uint8(sum[index+0] / n)
+ pix[1] = uint8(sum[index+1] / n)
+ pix[2] = uint8(sum[index+2] / n)
+ pix[3] = uint8(sum[index+3] / n)
+ }
+ }
+ return ret
+}
+
+// ResizeRGBA returns a scaled copy of the RGBA image slice r of m.
+// The returned image has width w and height h.
+func ResizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) *image.RGBA {
+ ww, hh := uint64(w), uint64(h)
+ dx, dy := uint64(r.Dx()), uint64(r.Dy())
+ // See comment in Resize.
+ n, sum := dx*dy, make([]uint64, 4*w*h)
+ for y := r.Min.Y; y < r.Max.Y; y++ {
+ pix := m.Pix[(y-r.Min.Y)*m.Stride:]
+ for x := r.Min.X; x < r.Max.X; x++ {
+ // Get the source pixel.
+ p := pix[(x-r.Min.X)*4:]
+ r64 := uint64(p[0])
+ g64 := uint64(p[1])
+ b64 := uint64(p[2])
+ a64 := uint64(p[3])
+ // Spread the source pixel over 1 or more destination rows.
+ py := uint64(y) * hh
+ for remy := hh; remy > 0; {
+ qy := dy - (py % dy)
+ if qy > remy {
+ qy = remy
+ }
+ // Spread the source pixel over 1 or more destination columns.
+ px := uint64(x) * ww
+ index := 4 * ((py/dy)*ww + (px / dx))
+ for remx := ww; remx > 0; {
+ qx := dx - (px % dx)
+ if qx > remx {
+ qx = remx
+ }
+ qxy := qx * qy
+ sum[index+0] += r64 * qxy
+ sum[index+1] += g64 * qxy
+ sum[index+2] += b64 * qxy
+ sum[index+3] += a64 * qxy
+ index += 4
+ px += qx
+ remx -= qx
+ }
+ py += qy
+ remy -= qy
+ }
+ }
+ }
+ return average(sum, w, h, n)
+}
+
+// ResizeNRGBA returns a scaled copy of the RGBA image slice r of m.
+// The returned image has width w and height h.
+func ResizeNRGBA(m *image.NRGBA, r image.Rectangle, w, h int) *image.RGBA {
+ ww, hh := uint64(w), uint64(h)
+ dx, dy := uint64(r.Dx()), uint64(r.Dy())
+ // See comment in Resize.
+ n, sum := dx*dy, make([]uint64, 4*w*h)
+ for y := r.Min.Y; y < r.Max.Y; y++ {
+ pix := m.Pix[(y-r.Min.Y)*m.Stride:]
+ for x := r.Min.X; x < r.Max.X; x++ {
+ // Get the source pixel.
+ p := pix[(x-r.Min.X)*4:]
+ r64 := uint64(p[0])
+ g64 := uint64(p[1])
+ b64 := uint64(p[2])
+ a64 := uint64(p[3])
+ r64 = (r64 * a64) / 255
+ g64 = (g64 * a64) / 255
+ b64 = (b64 * a64) / 255
+ // Spread the source pixel over 1 or more destination rows.
+ py := uint64(y) * hh
+ for remy := hh; remy > 0; {
+ qy := dy - (py % dy)
+ if qy > remy {
+ qy = remy
+ }
+ // Spread the source pixel over 1 or more destination columns.
+ px := uint64(x) * ww
+ index := 4 * ((py/dy)*ww + (px / dx))
+ for remx := ww; remx > 0; {
+ qx := dx - (px % dx)
+ if qx > remx {
+ qx = remx
+ }
+ qxy := qx * qy
+ sum[index+0] += r64 * qxy
+ sum[index+1] += g64 * qxy
+ sum[index+2] += b64 * qxy
+ sum[index+3] += a64 * qxy
+ index += 4
+ px += qx
+ remx -= qx
+ }
+ py += qy
+ remy -= qy
+ }
+ }
+ }
+ return average(sum, w, h, n)
+}
+
+// Resample returns a resampled copy of the image slice r of m.
+// The returned image has width w and height h.
+func Resample(m image.Image, r image.Rectangle, w, h int) *image.RGBA {
+ if w < 0 || h < 0 {
+ return nil
+ }
+ if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
+ return image.NewRGBA(image.Rect(0, 0, w, h))
+ }
+ curw, curh := r.Dx(), r.Dy()
+ img := image.NewRGBA(image.Rect(0, 0, w, h))
+ for y := 0; y < h; y++ {
+ for x := 0; x < w; x++ {
+ // Get a source pixel.
+ subx := x * curw / w
+ suby := y * curh / h
+ r32, g32, b32, a32 := m.At(subx, suby).RGBA()
+ r := uint8(r32 >> 8)
+ g := uint8(g32 >> 8)
+ b := uint8(b32 >> 8)
+ a := uint8(a32 >> 8)
+ img.SetRGBA(x, y, color.RGBA{r, g, b, a})
+ }
+ }
+ return img
+}