reprogl

Форк
0
/
generator.go 
206 строк · 5.1 Кб
1
package avatar
2

3
import (
4
	"crypto/md5"
5
	"errors"
6
	"fmt"
7
	"image"
8
	"image/draw"
9
	_ "image/jpeg"
10
	_ "image/png"
11
	"log"
12
	"os"
13
	"strconv"
14

15
	xdraw "golang.org/x/image/draw"
16
	"xelbot.com/reprogl/container"
17
	"xelbot.com/reprogl/models/repositories"
18
	"xelbot.com/reprogl/utils/hashid"
19
)
20

21
var (
22
	iconsConfig = map[string]map[string]int{
23
		"male": {
24
			"eye":        33,
25
			"hair":       36,
26
			"mouth":      26,
27
			"clothes":    64,
28
			"face":       4,
29
			"background": 5,
30
		},
31
		"female": {
32
			"eye":        53,
33
			"head":       34,
34
			"mouth":      17,
35
			"clothes":    59,
36
			"face":       4,
37
			"background": 5,
38
		},
39
	}
40
	validSizes = map[int]bool{
41
		32:  true,
42
		80:  true,
43
		160: true,
44
		200: true,
45
	}
46

47
	InvalidSize = errors.New("avatar: invalid size")
48
)
49

50
type dataDict map[string]string
51

52
func GenerateAvatar(hashData hashid.HashData, app *container.Application, size int) (image.Image, error) {
53
	if _, ok := validSizes[size]; !ok {
54
		return nil, InvalidSize
55
	}
56

57
	app.InfoLog.Printf("[IMG] avatar generation by %+v\n", hashData)
58
	if hashData.IsUser() {
59
		imageSrc, err := tryUserSource(hashData.ID, size)
60
		if err == nil && imageSrc != nil {
61
			app.InfoLog.Printf("[IMG] avatar %s found on public/data/pictures\n", hashData.Hash)
62

63
			return imageSrc, nil
64
		}
65
	}
66

67
	var object MaybeGravatar
68
	var err error
69
	if !hashData.IsUser() {
70
		repository := repositories.CommentRepository{DB: app.DB}
71
		object, err = repository.FindForGravatar(hashData.ID)
72
		if err != nil {
73
			return nil, err
74
		}
75
	} else {
76
		repository := repositories.UserRepository{DB: app.DB}
77
		object, err = repository.Find(hashData.ID)
78
		if err != nil {
79
			return nil, err
80
		}
81
	}
82

83
	gravatar, err := tryGravatar(object, size, app.InfoLog)
84
	if err == nil {
85
		app.InfoLog.Printf("[IMG] avatar %s found on gravatar.com\n", hashData.Hash)
86

87
		return gravatar, nil
88
	}
89

90
	return generate8BitIconAvatar(hashData.Hash, hashData.IsMale(), size, app.InfoLog)
91
}
92

93
func generate8BitIconAvatar(hash string, male bool, size int, logger *log.Logger) (image.Image, error) {
94
	dict := dataByHash(hash, male)
95
	logger.Printf("[IMG] details for %s: %+v\n", hash, dict)
96

97
	baseImage := image.NewRGBA(image.Rect(0, 0, 400, 400))
98

99
	bgImage, err := loadImage(filePath(dict, "background"))
100
	if err != nil {
101
		return nil, err
102
	}
103
	faceImage, err := loadImage(filePath(dict, "face"))
104
	if err != nil {
105
		return nil, err
106
	}
107
	clothesImage, err := loadImage(filePath(dict, "clothes"))
108
	if err != nil {
109
		return nil, err
110
	}
111
	mouthImage, err := loadImage(filePath(dict, "mouth"))
112
	if err != nil {
113
		return nil, err
114
	}
115

116
	var hairImage image.Image
117
	if male {
118
		hairImage, err = loadImage(filePath(dict, "hair"))
119
		if err != nil {
120
			return nil, err
121
		}
122
	} else {
123
		hairImage, err = loadImage(filePath(dict, "head"))
124
		if err != nil {
125
			return nil, err
126
		}
127
	}
128

129
	eyeImage, err := loadImage(filePath(dict, "eye"))
130
	if err != nil {
131
		return nil, err
132
	}
133

134
	draw.Draw(baseImage, bgImage.Bounds(), bgImage, image.Point{}, draw.Src)
135
	draw.Draw(baseImage, faceImage.Bounds(), faceImage, image.Point{}, draw.Over)
136
	draw.Draw(baseImage, clothesImage.Bounds(), clothesImage, image.Point{}, draw.Over)
137
	draw.Draw(baseImage, mouthImage.Bounds(), mouthImage, image.Point{}, draw.Over)
138
	draw.Draw(baseImage, hairImage.Bounds(), hairImage, image.Point{}, draw.Over)
139
	draw.Draw(baseImage, eyeImage.Bounds(), eyeImage, image.Point{}, draw.Over)
140

141
	imageResult := image.NewRGBA(image.Rect(0, 0, size, size))
142
	xdraw.NearestNeighbor.Scale(imageResult, imageResult.Bounds(), baseImage, baseImage.Bounds(), draw.Src, nil)
143

144
	return imageResult, nil
145
}
146

147
func dataByHash(hash string, male bool) dataDict {
148
	var gender string
149
	md5bytes := md5.Sum([]byte(hash))
150

151
	if male {
152
		gender = "male"
153
	} else {
154
		gender = "female"
155
	}
156

157
	facePart := int(md5bytes[0])
158
	backgroundPart := (int(md5bytes[1]) << 16) + (int(md5bytes[2]) << 8) + int(md5bytes[3])
159
	eyePart := (int(md5bytes[4]) << 16) + (int(md5bytes[5]) << 8) + int(md5bytes[6])
160
	hairPart := (int(md5bytes[7]) << 16) + (int(md5bytes[8]) << 8) + int(md5bytes[9])
161
	mouthPart := (int(md5bytes[10]) << 16) + (int(md5bytes[11]) << 8) + int(md5bytes[12])
162
	clothesPart := (int(md5bytes[13]) << 16) + (int(md5bytes[14]) << 8) + int(md5bytes[15])
163

164
	face := 1 + facePart%iconsConfig[gender]["face"]
165
	eye := 1 + eyePart%iconsConfig[gender]["eye"]
166
	mouth := 1 + mouthPart%iconsConfig[gender]["mouth"]
167
	clothes := 1 + clothesPart%iconsConfig[gender]["clothes"]
168
	background := 1 + backgroundPart%iconsConfig[gender]["background"]
169

170
	dict := dataDict{
171
		"gender":     gender,
172
		"face":       strconv.Itoa(face),
173
		"eye":        strconv.Itoa(eye),
174
		"mouth":      strconv.Itoa(mouth),
175
		"clothes":    strconv.Itoa(clothes),
176
		"background": strconv.Itoa(background),
177
	}
178

179
	if male {
180
		dict["hair"] = strconv.Itoa(1 + hairPart%iconsConfig[gender]["hair"])
181
	} else {
182
		dict["head"] = strconv.Itoa(1 + hairPart%iconsConfig[gender]["head"])
183
	}
184

185
	return dict
186
}
187

188
func filePath(dict dataDict, key string) string {
189
	return fmt.Sprintf("./var/8biticon/%s/%s%s.png", dict["gender"], key, dict[key])
190
}
191

192
func loadImage(filePath string) (image.Image, error) {
193
	imgFile, err := os.Open(filePath)
194
	defer imgFile.Close()
195

196
	if err != nil {
197
		return nil, err
198
	}
199

200
	img, _, err := image.Decode(imgFile)
201
	if err != nil {
202
		return nil, err
203
	}
204

205
	return img, nil
206
}
207

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.