avatars/main.go
nisan 8220922cc9
All checks were successful
Publish Docker Image / build_push (push) Successful in 1m39s
initial commit 🚀
2024-07-31 21:00:37 +03:00

144 lines
3.5 KiB
Go

package main
import (
"bytes"
"embed"
"hash/fnv"
"image"
"image/color"
"image/jpeg"
_ "image/png"
"io/fs"
"log"
"net/http"
"strconv"
"strings"
"time"
"golang.org/x/image/draw"
)
//go:embed assets/*
var files embed.FS
var BaseRects [11]draw.Image
var EYES = make([]image.Image, 0)
var NOSE = make([]image.Image, 0)
var MOUTH = make([]image.Image, 0)
var emptyRect = image.Rect(0, 0, 400, 400)
func getPartsById(id string) (draw.Image, image.Image, image.Image, image.Image) {
idBytes := []byte(id)
colorHash := fnv.New32a()
colorHash.Write(idBytes)
eyesHash := fnv.New32a()
eyesHash.Write(idBytes)
noseHash := fnv.New32a()
noseHash.Write(idBytes)
mouthHash := fnv.New32a()
mouthHash.Write(idBytes)
return BaseRects[colorHash.Sum32()%11],
EYES[eyesHash.Sum32()%9],
NOSE[noseHash.Sum32()%8],
MOUTH[mouthHash.Sum32()%8]
}
func serveAvatar(w http.ResponseWriter, r *http.Request) {
startTime := time.Now()
// trollin' attackers
w.Header().Set("x-powered-by", "Undertow/1")
w.Header().Set("server", "JBoss-EAP/7")
// get the id and size from the request
id := r.PathValue("id")
size, err := strconv.Atoi(r.PathValue("size"))
if err != nil {
size = 400
}
// min size is 32, max size is 1000
if size < 32 {
size = 32
} else if size > 1000 {
size = 1000
}
img := image.NewRGBA(emptyRect)
// draw parts over base(color) image
base, eyes, nose, mouth := getPartsById(id)
draw.Draw(img, emptyRect, base, emptyRect.Min, draw.Src)
draw.Draw(img, emptyRect, eyes, emptyRect.Min, draw.Over)
draw.Draw(img, emptyRect, nose, emptyRect.Min, draw.Over)
draw.Draw(img, emptyRect, mouth, emptyRect.Min, draw.Over)
if size != 400 {
// resize the image
dst := image.NewRGBA(image.Rect(0, 0, size, size))
draw.ApproxBiLinear.Scale(dst, dst.Bounds(), img, emptyRect, draw.Over, nil)
img = dst
}
// write the image to the response
w.Header().Set("content-type", "image/jpeg")
w.WriteHeader(http.StatusCreated)
err = jpeg.Encode(w, img, nil)
if err != nil {
log.Println(err.Error())
}
log.Printf("[id:%s][size:%d] served to [%s] [%s] [%v]\n", id, size, r.RemoteAddr, r.Proto, time.Since(startTime))
}
func main() {
log.Println("assets loaded")
mux := http.NewServeMux()
mux.HandleFunc("GET /{id}", serveAvatar)
mux.HandleFunc("GET /{size}/{id}", serveAvatar)
http.ListenAndServe("0.0.0.0:3382", mux)
}
func init() {
_ = fs.WalkDir(files, "assets", func(path string, d fs.DirEntry, _ error) error {
if !d.IsDir() {
file, _ := files.ReadFile(path)
img, _, _ := image.Decode(bytes.NewReader(file))
if strings.Contains(path, "/eyes/eyes") {
EYES = append(EYES, img)
} else if strings.Contains(path, "/nose/nose") {
NOSE = append(NOSE, img)
} else if strings.Contains(path, "/mouth/mouth") {
MOUTH = append(MOUTH, img)
}
}
return nil
})
colors := [11]color.RGBA{
{129, 190, 241, 255},
{173, 139, 242, 255},
{191, 242, 136, 255},
{222, 120, 120, 255},
{165, 170, 197, 255},
{111, 242, 197, 255},
{240, 218, 94, 255},
{235, 89, 114, 255},
{246, 190, 93, 255},
{0, 185, 189, 255},
{251, 105, 0, 255},
}
//init Base Rectangles
for i := 0; i < 11; i++ {
BaseRects[i] = image.NewRGBA(image.Rect(0, 0, 400, 400))
for t := 0; t < 200; t++ {
// draw horizontal lines
for x := 0; x <= 399; x++ {
BaseRects[i].Set(x, 0+t, colors[i])
BaseRects[i].Set(x, 399-t, colors[i])
}
// draw vertical lines
for y := 0; y <= 399; y++ {
BaseRects[i].Set(0+t, y, colors[i])
BaseRects[i].Set(399-t, y, colors[i])
}
}
}
}