1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
package main
import (
"bytes"
"crypto/sha256"
"errors"
"image"
"image/color"
_ "image/gif"
_ "image/jpeg"
"image/png"
"math"
"sync"
"golang.org/x/image/draw"
_ "golang.org/x/image/webp"
)
const avatarSize = 256
var decoding sync.Mutex
func cropAvatar(data []byte, x, y, size int) ([]byte, error) {
cfg, _, err := image.DecodeConfig(bytes.NewReader(data))
if err != nil {
return nil, errors.New("Avatar must be a PNG, JPEG, GIF or WebP image")
}
if cfg.Width > 6000 || cfg.Height > 6000 || cfg.Width*cfg.Height > 16_000_000 {
return nil, errors.New("Avatar image is too large, keep it under 16 megapixels")
}
decoding.Lock()
defer decoding.Unlock()
img, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return nil, errors.New("Avatar must be a PNG, JPEG, GIF or WebP image")
}
b := img.Bounds()
if size <= 0 || size > b.Dx() || size > b.Dy() {
size = min(b.Dx(), b.Dy())
x = (b.Dx() - size) / 2
y = (b.Dy() - size) / 2
}
x = max(0, min(x, b.Dx()-size))
y = max(0, min(y, b.Dy()-size))
src := image.Rect(b.Min.X+x, b.Min.Y+y, b.Min.X+x+size, b.Min.Y+y+size)
dst := image.NewRGBA(image.Rect(0, 0, avatarSize, avatarSize))
draw.CatmullRom.Scale(dst, dst.Bounds(), img, src, draw.Src, nil)
var out bytes.Buffer
if err := png.Encode(&out, dst); err != nil {
return nil, err
}
return out.Bytes(), nil
}
func identicon(seed string) []byte {
sum := sha256.Sum256([]byte(seed))
h := float64(uint16(sum[28])<<8|uint16(sum[29])) / 65536
fg := hsl(h, 0.45+float64(sum[30])/255*0.2, 0.5+float64(sum[31])/255*0.12)
const cell, pad = 44, 18
img := image.NewRGBA(image.Rect(0, 0, avatarSize, avatarSize))
draw.Draw(img, img.Bounds(), &image.Uniform{color.RGBA{240, 240, 240, 255}}, image.Point{}, draw.Src)
for i := 0; i < 15; i++ {
if sum[i]&1 != 0 {
continue
}
row, col := i%5, i/5
for _, c := range []int{col, 4 - col} {
r := image.Rect(pad+c*cell, pad+row*cell, pad+(c+1)*cell, pad+(row+1)*cell)
draw.Draw(img, r, &image.Uniform{fg}, image.Point{}, draw.Src)
}
}
var out bytes.Buffer
png.Encode(&out, img)
return out.Bytes()
}
func hsl(h, s, l float64) color.RGBA {
q := l + s - l*s
if l < 0.5 {
q = l * (1 + s)
}
p := 2*l - q
ch := func(t float64) uint8 {
t -= math.Floor(t)
switch {
case t < 1.0/6:
return uint8(255 * (p + (q-p)*6*t))
case t < 0.5:
return uint8(255 * q)
case t < 2.0/3:
return uint8(255 * (p + (q-p)*(2.0/3-t)*6))
}
return uint8(255 * p)
}
return color.RGBA{ch(h + 1.0/3), ch(h), ch(h - 1.0/3), 255}
}
|