@@ 29,9 29,9 @@ import (
"strconv"
"strings"
+ "github.com/cespare/xxhash/v2"
"github.com/gorilla/mux"
adorable "github.com/ipsn/go-adorable"
- "github.com/aofei/cameron"
)
var avatarcolors = [4][4]byte{
@@ 77,7 77,7 @@ func genAvatara(name string) []byte {
func genAvatar(name string) []byte {
img := bytes.Buffer{}
- png.Encode(&img, cameron.Identicon([]byte(name), 540, 60))
+ png.Encode(&img, Identicon([]byte(name), 540, 60))
return img.Bytes()
}
@@ 198,3 198,63 @@ func renderflags(h *Honk) {
return fmt.Sprintf(`<img class="emu" title="%s" src="%s">`, "flag", src)
})
}
+
+// Identicon returns an identicon avatar based on the `data` with the `length`
+// and `blockLength`.
+func Identicon(data []byte, length, blockLength int) image.Image {
+ digest := xxhash.Sum64(data)
+ img := image.NewPaletted(
+ image.Rect(0, 0, length, length),
+ color.Palette{
+ color.NRGBA{
+ R: 0xff,
+ G: 0xff,
+ B: 0xee,
+ A: 0xff,
+ },
+ color.NRGBA{
+ R: 0,
+ G: 0,
+ B: 0xff,
+ A: 0xff,
+ },
+ },
+ )
+
+ if blockLength > length {
+ blockLength = length
+ }
+
+ columnsCount := length / blockLength
+ padding := blockLength / 2
+ if length%blockLength != 0 {
+ padding = (length - blockLength*columnsCount) / 2
+ } else if columnsCount > 1 {
+ columnsCount--
+ } else {
+ padding = 0
+ }
+
+ filled := columnsCount == 1
+ pixels := bytes.Repeat([]byte{1}, blockLength)
+ for i, ri, ci := 0, 0, 0; i < columnsCount*(columnsCount+1)/2; i++ {
+ if filled || digest>>uint(i%64)&1 == 1 {
+ for i := 0; i < blockLength; i++ {
+ x := padding + ri*blockLength
+ y := padding + ci*blockLength + i
+ copy(img.Pix[img.PixOffset(x, y):], pixels)
+
+ x = padding + (columnsCount-1-ri)*blockLength
+ copy(img.Pix[img.PixOffset(x, y):], pixels)
+ }
+ }
+
+ ci++
+ if ci == columnsCount {
+ ci = 0
+ ri++
+ }
+ }
+
+ return img
+}