avatar.go (view raw)
1//
2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16package main
17
18import (
19 "bufio"
20 "bytes"
21 "crypto/sha512"
22 "fmt"
23 "image"
24 "image/png"
25 "log"
26 "net/http"
27 "regexp"
28 "strconv"
29 "strings"
30
31 "github.com/gorilla/mux"
32)
33
34var avatarcolors = [4][4]byte{
35 {16, 0, 48, 255},
36 {48, 0, 96, 255},
37 {72, 0, 144, 255},
38 {96, 0, 192, 255},
39}
40
41func loadAvatarColors() {
42 var colors string
43 getconfig("avatarcolors", &colors)
44 if colors == "" {
45 return
46 }
47 r := bufio.NewReader(strings.NewReader(colors))
48 for i := 0; i < 4; i++ {
49 l, _ := r.ReadString(' ')
50 for l == " " {
51 l, _ = r.ReadString(' ')
52 }
53 l = strings.Trim(l, "# \n")
54 if len(l) == 6 {
55 l = l + "ff"
56 }
57 c, err := strconv.ParseUint(l, 16, 32)
58 if err != nil {
59 log.Printf("error reading avatar color %d: %s", i, err)
60 continue
61 }
62 avatarcolors[i][0] = byte(c >> 24 & 0xff)
63 avatarcolors[i][1] = byte(c >> 16 & 0xff)
64 avatarcolors[i][2] = byte(c >> 8 & 0xff)
65 avatarcolors[i][3] = byte(c >> 0 & 0xff)
66 }
67}
68
69func genAvatar(name string) []byte {
70 h := sha512.New()
71 h.Write([]byte(name))
72 s := h.Sum(nil)
73 img := image.NewNRGBA(image.Rect(0, 0, 64, 64))
74 for i := 0; i < 64; i++ {
75 for j := 0; j < 64; j++ {
76 p := i*img.Stride + j*4
77 xx := i/16*16 + j/16
78 x := s[xx]
79 if x < 64 {
80 img.Pix[p+0] = avatarcolors[0][0]
81 img.Pix[p+1] = avatarcolors[0][1]
82 img.Pix[p+2] = avatarcolors[0][2]
83 img.Pix[p+3] = avatarcolors[0][3]
84 } else if x < 128 {
85 img.Pix[p+0] = avatarcolors[1][0]
86 img.Pix[p+1] = avatarcolors[1][1]
87 img.Pix[p+2] = avatarcolors[1][2]
88 img.Pix[p+3] = avatarcolors[1][3]
89 } else if x < 192 {
90 img.Pix[p+0] = avatarcolors[2][0]
91 img.Pix[p+1] = avatarcolors[2][1]
92 img.Pix[p+2] = avatarcolors[2][2]
93 img.Pix[p+3] = avatarcolors[2][3]
94 } else {
95 img.Pix[p+0] = avatarcolors[3][0]
96 img.Pix[p+1] = avatarcolors[3][1]
97 img.Pix[p+2] = avatarcolors[3][2]
98 img.Pix[p+3] = avatarcolors[3][3]
99 }
100 }
101 }
102 var buf bytes.Buffer
103 png.Encode(&buf, img)
104 return buf.Bytes()
105}
106
107func showflag(writer http.ResponseWriter, req *http.Request) {
108 code := mux.Vars(req)["code"]
109 colors := strings.Split(code, ",")
110 numcolors := len(colors)
111 vert := false
112 if colors[0] == "vert" {
113 vert = true
114 colors = colors[1:]
115 numcolors--
116 if numcolors == 0 {
117 http.Error(writer, "bad flag", 400)
118 return
119 }
120 }
121 pixels := make([][4]byte, numcolors)
122 for i := 0; i < numcolors; i++ {
123 hex := colors[i]
124 if len(hex) == 3 {
125 hex = fmt.Sprintf("%c%c%c%c%c%c",
126 hex[0], hex[0], hex[1], hex[1], hex[2], hex[2])
127 }
128 c, _ := strconv.ParseUint(hex, 16, 32)
129 r := byte(c >> 16 & 0xff)
130 g := byte(c >> 8 & 0xff)
131 b := byte(c >> 0 & 0xff)
132 pixels[i][0] = r
133 pixels[i][1] = g
134 pixels[i][2] = b
135 pixels[i][3] = 255
136 }
137
138 h := 128
139 w := h * 3 / 2
140 img := image.NewRGBA(image.Rect(0, 0, w, h))
141 if vert {
142 for j := 0; j < w; j++ {
143 pix := pixels[j*numcolors/w][:]
144 for i := 0; i < h; i++ {
145 p := i*img.Stride + j*4
146 copy(img.Pix[p:], pix)
147 }
148 }
149 } else {
150 for i := 0; i < h; i++ {
151 pix := pixels[i*numcolors/h][:]
152 for j := 0; j < w; j++ {
153 p := i*img.Stride + j*4
154 copy(img.Pix[p:], pix)
155 }
156 }
157 }
158
159 writer.Header().Set("Cache-Control", "max-age="+somedays())
160 png.Encode(writer, img)
161}
162
163var re_flags = regexp.MustCompile("flag:[[:alnum:],]+")
164
165func fixupflags(h *Honk) []Emu {
166 var emus []Emu
167 count := 0
168 h.Noise = re_flags.ReplaceAllStringFunc(h.Noise, func(m string) string {
169 count++
170 var e Emu
171 e.Name = fmt.Sprintf(":flag%d:", count)
172 e.ID = fmt.Sprintf("https://%s/flag/%s", serverName, m[5:])
173 emus = append(emus, e)
174 return e.Name
175 })
176 return emus
177}
178
179func renderflags(h *Honk) {
180 h.Noise = re_flags.ReplaceAllStringFunc(h.Noise, func(m string) string {
181 code := m[5:]
182 src := fmt.Sprintf("https://%s/flag/%s", serverName, code)
183 return fmt.Sprintf(`<img class="emu" title="%s" src="%s">`, "flag", src)
184 })
185}