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 "net/http"
26 "regexp"
27 "strconv"
28 "strings"
29
30 "github.com/gorilla/mux"
31)
32
33var avatarcolors = [4][4]byte{
34 {16, 0, 48, 255},
35 {48, 0, 96, 255},
36 {72, 0, 144, 255},
37 {96, 0, 192, 255},
38}
39
40func loadAvatarColors() {
41 var colors string
42 getconfig("avatarcolors", &colors)
43 if colors == "" {
44 return
45 }
46 r := bufio.NewReader(strings.NewReader(colors))
47 for i := 0; i < 4; i++ {
48 l, _ := r.ReadString(' ')
49 for l == " " {
50 l, _ = r.ReadString(' ')
51 }
52 l = strings.Trim(l, "# \n")
53 if len(l) == 6 {
54 l = l + "ff"
55 }
56 c, err := strconv.ParseUint(l, 16, 32)
57 if err != nil {
58 elog.Printf("error reading avatar color %d: %s", i, err)
59 continue
60 }
61 avatarcolors[i][0] = byte(c >> 24 & 0xff)
62 avatarcolors[i][1] = byte(c >> 16 & 0xff)
63 avatarcolors[i][2] = byte(c >> 8 & 0xff)
64 avatarcolors[i][3] = byte(c >> 0 & 0xff)
65 }
66}
67
68func genAvatar(name string, hex bool) []byte {
69 h := sha512.New()
70 h.Write([]byte(name))
71 s := h.Sum(nil)
72 img := image.NewNRGBA(image.Rect(0, 0, 64, 64))
73 for i := 0; i < 64; i++ {
74 for j := 0; j < 64; j++ {
75 p := i*img.Stride + j*4
76 if hex {
77 tan := 0.577
78 if i < 32 {
79 if j < 17-int(float64(i)*tan) || j > 46+int(float64(i)*tan) {
80 img.Pix[p+0] = 0
81 img.Pix[p+1] = 0
82 img.Pix[p+2] = 0
83 img.Pix[p+3] = 255
84 continue
85 }
86 } else {
87 if j < 17-int(float64(64-i)*tan) || j > 46+int(float64(64-i)*tan) {
88 img.Pix[p+0] = 0
89 img.Pix[p+1] = 0
90 img.Pix[p+2] = 0
91 img.Pix[p+3] = 255
92 continue
93
94 }
95 }
96 }
97 xx := i/16*16 + j/16
98 x := s[xx]
99 if x < 64 {
100 img.Pix[p+0] = avatarcolors[0][0]
101 img.Pix[p+1] = avatarcolors[0][1]
102 img.Pix[p+2] = avatarcolors[0][2]
103 img.Pix[p+3] = avatarcolors[0][3]
104 } else if x < 128 {
105 img.Pix[p+0] = avatarcolors[1][0]
106 img.Pix[p+1] = avatarcolors[1][1]
107 img.Pix[p+2] = avatarcolors[1][2]
108 img.Pix[p+3] = avatarcolors[1][3]
109 } else if x < 192 {
110 img.Pix[p+0] = avatarcolors[2][0]
111 img.Pix[p+1] = avatarcolors[2][1]
112 img.Pix[p+2] = avatarcolors[2][2]
113 img.Pix[p+3] = avatarcolors[2][3]
114 } else {
115 img.Pix[p+0] = avatarcolors[3][0]
116 img.Pix[p+1] = avatarcolors[3][1]
117 img.Pix[p+2] = avatarcolors[3][2]
118 img.Pix[p+3] = avatarcolors[3][3]
119 }
120 }
121 }
122 var buf bytes.Buffer
123 png.Encode(&buf, img)
124 return buf.Bytes()
125}
126
127func showflag(writer http.ResponseWriter, req *http.Request) {
128 code := mux.Vars(req)["code"]
129 colors := strings.Split(code, ",")
130 numcolors := len(colors)
131 vert := false
132 if colors[0] == "vert" {
133 vert = true
134 colors = colors[1:]
135 numcolors--
136 if numcolors == 0 {
137 http.Error(writer, "bad flag", 400)
138 return
139 }
140 }
141 pixels := make([][4]byte, numcolors)
142 for i := 0; i < numcolors; i++ {
143 hex := colors[i]
144 if len(hex) == 3 {
145 hex = fmt.Sprintf("%c%c%c%c%c%c",
146 hex[0], hex[0], hex[1], hex[1], hex[2], hex[2])
147 }
148 c, _ := strconv.ParseUint(hex, 16, 32)
149 r := byte(c >> 16 & 0xff)
150 g := byte(c >> 8 & 0xff)
151 b := byte(c >> 0 & 0xff)
152 pixels[i][0] = r
153 pixels[i][1] = g
154 pixels[i][2] = b
155 pixels[i][3] = 255
156 }
157
158 h := 128
159 w := h * 3 / 2
160 img := image.NewRGBA(image.Rect(0, 0, w, h))
161 if vert {
162 for j := 0; j < w; j++ {
163 pix := pixels[j*numcolors/w][:]
164 for i := 0; i < h; i++ {
165 p := i*img.Stride + j*4
166 copy(img.Pix[p:], pix)
167 }
168 }
169 } else {
170 for i := 0; i < h; i++ {
171 pix := pixels[i*numcolors/h][:]
172 for j := 0; j < w; j++ {
173 p := i*img.Stride + j*4
174 copy(img.Pix[p:], pix)
175 }
176 }
177 }
178
179 writer.Header().Set("Cache-Control", "max-age="+somedays())
180 png.Encode(writer, img)
181}
182
183var re_flags = regexp.MustCompile("flag:[[:alnum:],]+")
184
185func fixupflags(h *Honk) []Emu {
186 var emus []Emu
187 count := 0
188 h.Noise = re_flags.ReplaceAllStringFunc(h.Noise, func(m string) string {
189 count++
190 var e Emu
191 e.Name = fmt.Sprintf(":flag%d:", count)
192 e.ID = fmt.Sprintf("https://%s/flag/%s", serverName, m[5:])
193 emus = append(emus, e)
194 return e.Name
195 })
196 return emus
197}
198
199func renderflags(h *Honk) {
200 h.Noise = re_flags.ReplaceAllStringFunc(h.Noise, func(m string) string {
201 code := m[5:]
202 src := fmt.Sprintf("https://%s/flag/%s", serverName, code)
203 return fmt.Sprintf(`<img class="emu" title="%s" src="%s">`, "flag", src)
204 })
205}