all repos — honk @ a20e95b0574a454fd51195657e01bb3784ff59ee

my fork of honk

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}