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, hex bool) []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 if hex {
78 tan := 0.577
79 if i < 32 {
80 if j < 17-int(float64(i)*tan) || j > 46+int(float64(i)*tan) {
81 img.Pix[p+0] = 0
82 img.Pix[p+1] = 0
83 img.Pix[p+2] = 0
84 img.Pix[p+3] = 255
85 continue
86 }
87 } else {
88 if j < 17-int(float64(64-i)*tan) || j > 46+int(float64(64-i)*tan) {
89 img.Pix[p+0] = 0
90 img.Pix[p+1] = 0
91 img.Pix[p+2] = 0
92 img.Pix[p+3] = 255
93 continue
94
95 }
96 }
97 }
98 xx := i/16*16 + j/16
99 x := s[xx]
100 if x < 64 {
101 img.Pix[p+0] = avatarcolors[0][0]
102 img.Pix[p+1] = avatarcolors[0][1]
103 img.Pix[p+2] = avatarcolors[0][2]
104 img.Pix[p+3] = avatarcolors[0][3]
105 } else if x < 128 {
106 img.Pix[p+0] = avatarcolors[1][0]
107 img.Pix[p+1] = avatarcolors[1][1]
108 img.Pix[p+2] = avatarcolors[1][2]
109 img.Pix[p+3] = avatarcolors[1][3]
110 } else if x < 192 {
111 img.Pix[p+0] = avatarcolors[2][0]
112 img.Pix[p+1] = avatarcolors[2][1]
113 img.Pix[p+2] = avatarcolors[2][2]
114 img.Pix[p+3] = avatarcolors[2][3]
115 } else {
116 img.Pix[p+0] = avatarcolors[3][0]
117 img.Pix[p+1] = avatarcolors[3][1]
118 img.Pix[p+2] = avatarcolors[3][2]
119 img.Pix[p+3] = avatarcolors[3][3]
120 }
121 }
122 }
123 var buf bytes.Buffer
124 png.Encode(&buf, img)
125 return buf.Bytes()
126}
127
128func showflag(writer http.ResponseWriter, req *http.Request) {
129 code := mux.Vars(req)["code"]
130 colors := strings.Split(code, ",")
131 numcolors := len(colors)
132 vert := false
133 if colors[0] == "vert" {
134 vert = true
135 colors = colors[1:]
136 numcolors--
137 if numcolors == 0 {
138 http.Error(writer, "bad flag", 400)
139 return
140 }
141 }
142 pixels := make([][4]byte, numcolors)
143 for i := 0; i < numcolors; i++ {
144 hex := colors[i]
145 if len(hex) == 3 {
146 hex = fmt.Sprintf("%c%c%c%c%c%c",
147 hex[0], hex[0], hex[1], hex[1], hex[2], hex[2])
148 }
149 c, _ := strconv.ParseUint(hex, 16, 32)
150 r := byte(c >> 16 & 0xff)
151 g := byte(c >> 8 & 0xff)
152 b := byte(c >> 0 & 0xff)
153 pixels[i][0] = r
154 pixels[i][1] = g
155 pixels[i][2] = b
156 pixels[i][3] = 255
157 }
158
159 h := 128
160 w := h * 3 / 2
161 img := image.NewRGBA(image.Rect(0, 0, w, h))
162 if vert {
163 for j := 0; j < w; j++ {
164 pix := pixels[j*numcolors/w][:]
165 for i := 0; i < h; i++ {
166 p := i*img.Stride + j*4
167 copy(img.Pix[p:], pix)
168 }
169 }
170 } else {
171 for i := 0; i < h; i++ {
172 pix := pixels[i*numcolors/h][:]
173 for j := 0; j < w; j++ {
174 p := i*img.Stride + j*4
175 copy(img.Pix[p:], pix)
176 }
177 }
178 }
179
180 writer.Header().Set("Cache-Control", "max-age="+somedays())
181 png.Encode(writer, img)
182}
183
184var re_flags = regexp.MustCompile("flag:[[:alnum:],]+")
185
186func fixupflags(h *Honk) []Emu {
187 var emus []Emu
188 count := 0
189 h.Noise = re_flags.ReplaceAllStringFunc(h.Noise, func(m string) string {
190 count++
191 var e Emu
192 e.Name = fmt.Sprintf(":flag%d:", count)
193 e.ID = fmt.Sprintf("https://%s/flag/%s", serverName, m[5:])
194 emus = append(emus, e)
195 return e.Name
196 })
197 return emus
198}
199
200func renderflags(h *Honk) {
201 h.Noise = re_flags.ReplaceAllStringFunc(h.Noise, func(m string) string {
202 code := m[5:]
203 src := fmt.Sprintf("https://%s/flag/%s", serverName, code)
204 return fmt.Sprintf(`<img class="emu" title="%s" src="%s">`, "flag", src)
205 })
206}