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