audon/avatar.go

172 wiersze
4.3 KiB
Go

package main
import (
"bytes"
"context"
"crypto/sha256"
"errors"
"fmt"
"image"
"image/color"
"image/gif"
"image/jpeg"
"image/png"
"os"
"path/filepath"
"github.com/gabriel-vasile/mimetype"
"github.com/sizeofint/webpanimation"
"go.mongodb.org/mongo-driver/bson"
"golang.org/x/image/draw"
"golang.org/x/image/webp"
"gopkg.in/gographics/imagick.v2/imagick"
)
func (u *AudonUser) GetIndicator(ctx context.Context, fnew []byte, room *Room) (indicator []byte, original []byte, isGIF bool, err error) {
isGIF = false
if u == nil {
err = errors.New("nil user")
return
}
mtype := mimetype.Detect(fnew)
if !mimetype.EqualsAny(mtype.String(), "image/png", "image/jpeg", "image/webp", "image/gif") {
err = errors.New("file type not supported")
return
}
buf := bytes.NewReader(fnew)
var newImg image.Image
if mtype.Is("image/png") {
newImg, err = png.Decode(buf)
} else if mtype.Is("image/jpeg") {
newImg, err = jpeg.Decode(buf)
} else if mtype.Is("image/webp") {
newImg, err = webp.Decode(buf)
} else if mtype.Is("image/gif") {
newImg, err = gif.Decode(buf)
isGIF = true
}
if err != nil {
return
}
// encode to png to avoid recompression, except GIF
var origImg []byte
if !isGIF {
origBuf := new(bytes.Buffer)
if err = png.Encode(origBuf, newImg); err != nil {
return
}
origImg = origBuf.Bytes()
} else {
origImg = fnew
}
hash := sha256.Sum256(origImg)
// Check if user's original avatar exists
var filename string
if isGIF {
filename = fmt.Sprintf("%x.gif", hash)
} else {
filename = fmt.Sprintf("%x.png", hash)
}
saved := u.getAvatarImagePath(filename)
if _, err = os.Stat(saved); err != nil {
if err = os.MkdirAll(filepath.Dir(saved), 0775); err != nil {
return
}
// Write user's avatar if the original version doesn't exist
if err = os.WriteFile(saved, origImg, 0664); err != nil {
return
}
}
coll := mainDB.Collection(COLLECTION_USER)
if _, err = coll.UpdateOne(ctx,
bson.D{{Key: "audon_id", Value: u.AudonID}},
bson.D{
{Key: "$set", Value: bson.D{{Key: "avatar", Value: filename}}},
}); err != nil {
return
}
// indicator, err = u.createGIF(newImg, room.IsHost(u) || room.IsCoHost(u))
// if err != nil {
// return
// }
return indicator, origImg, isGIF, nil
}
func (u *AudonUser) createGIF(avatar image.Image, blue bool) ([]byte, error) {
avatarPNG := image.NewRGBA(image.Rect(0, 0, 150, 150))
draw.BiLinear.Scale(avatarPNG, avatarPNG.Rect, avatar, avatar.Bounds(), draw.Src, nil)
baseFrame := image.NewRGBA(avatarPNG.Bounds())
draw.Draw(baseFrame, baseFrame.Bounds(), image.Black, image.Point{}, draw.Src)
draw.Copy(baseFrame, image.Point{}, avatarPNG, avatarPNG.Bounds(), draw.Over, nil)
logoImageBack := mainConfig.LogoImageWhiteBack
if blue {
logoImageBack = mainConfig.LogoImageBlueBack
}
draw.Draw(baseFrame, baseFrame.Bounds(), logoImageBack, image.Point{-55, -105}, draw.Over)
anim := webpanimation.NewWebpAnimation(150, 150, 0)
defer anim.ReleaseMemory()
webpConf := webpanimation.NewWebpConfig()
webpConf.SetLossless(1)
count := 20
for i := 0; i < count; i++ {
frame := image.NewRGBA(baseFrame.Bounds())
draw.Copy(frame, image.Point{}, baseFrame, baseFrame.Bounds(), draw.Src, nil)
var alpha uint8
if i < count/2 {
alpha = uint8(255. * (1. - float32(2*i)/float32(count)))
} else {
alpha = uint8(255. * (float32(2*i)/float32(count) - 1.))
}
mask := image.NewUniform(color.Alpha{alpha})
draw.DrawMask(frame, frame.Bounds(), mainConfig.LogoImageFront, image.Point{-55, -105}, mask, image.Point{}, draw.Over)
if err := anim.AddFrame(frame, 1000/count*i, webpConf); err != nil {
return nil, err
}
}
outBuf, _ := os.Create(u.getWebPAvatarPath())
defer outBuf.Close()
anim.Encode(outBuf)
imagick.Initialize()
defer imagick.Terminate()
if _, err := imagick.ConvertImageCommand([]string{"convert", u.getWebPAvatarPath(), u.getGIFAvatarPath()}); err != nil {
return nil, err
}
return os.ReadFile(u.getGIFAvatarPath())
}
func (u *AudonUser) getGIFAvatarPath() string {
return u.getAvatarImagePath("indicator.gif")
}
func (u *AudonUser) getWebPAvatarPath() string {
return u.getAvatarImagePath("indicator.webp")
}
func (u *AudonUser) getAvatarImagePath(name string) string {
if u == nil {
return ""
}
return filepath.Join(mainConfig.StorageDir, u.AudonID, "avatar", name)
}