wip

embed image

wip
This commit is contained in:
Chris Sexton 2021-11-30 10:28:34 -05:00
parent 6d004d8d1f
commit 7199f8ed13
9 changed files with 376 additions and 52 deletions

View File

@ -51,6 +51,8 @@ type ImageAttachment struct {
Height int Height int
} }
type VideoAttachment ImageAttachment
type Request struct { type Request struct {
Conn Connector Conn Connector
Kind Kind Kind Kind

View File

@ -96,8 +96,15 @@ func (d *Discord) sendMessage(channel, message string, meMessage bool, args ...i
for _, arg := range args { for _, arg := range args {
switch a := arg.(type) { switch a := arg.(type) {
case bot.VideoAttachment:
embeds = &discordgo.MessageEmbed{}
embeds.Description = a.AltTxt
embeds.Video = &discordgo.MessageEmbedVideo{
URL: a.URL,
Width: a.Width,
Height: a.Height,
}
case bot.ImageAttachment: case bot.ImageAttachment:
//embeds.URL = a.URL
embeds = &discordgo.MessageEmbed{} embeds = &discordgo.MessageEmbed{}
embeds.Description = a.AltTxt embeds.Description = a.AltTxt
embeds.Image = &discordgo.MessageEmbedImage{ embeds.Image = &discordgo.MessageEmbedImage{

2
go.mod
View File

@ -9,6 +9,7 @@ require (
github.com/ChimeraCoder/tokenbucket v0.0.0-20131201223612-c5a927568de7 // indirect github.com/ChimeraCoder/tokenbucket v0.0.0-20131201223612-c5a927568de7 // indirect
github.com/PuerkitoBio/goquery v1.5.0 github.com/PuerkitoBio/goquery v1.5.0
github.com/andybalholm/cascadia v1.1.0 // indirect github.com/andybalholm/cascadia v1.1.0 // indirect
github.com/andybons/gogif v0.0.0-20140526152223-16d573594812 // indirect
github.com/antchfx/htmlquery v1.2.0 // indirect github.com/antchfx/htmlquery v1.2.0 // indirect
github.com/antchfx/xmlquery v1.2.0 // indirect github.com/antchfx/xmlquery v1.2.0 // indirect
github.com/antchfx/xpath v1.1.1 // indirect github.com/antchfx/xpath v1.1.1 // indirect
@ -30,6 +31,7 @@ require (
github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/gorilla/handlers v1.5.1 // indirect
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 // indirect github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 // indirect
github.com/itchyny/gojq v0.12.3 github.com/itchyny/gojq v0.12.3
github.com/james-bowman/nlp v0.0.0-20191016091239-d9dbfaff30c6 github.com/james-bowman/nlp v0.0.0-20191016091239-d9dbfaff30c6

6
go.sum
View File

@ -15,6 +15,8 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/andybons/gogif v0.0.0-20140526152223-16d573594812 h1:WBBv0ka2SO7Ut4bpskb87E9cHNnJabqA6VoBTex0Jng=
github.com/andybons/gogif v0.0.0-20140526152223-16d573594812/go.mod h1:lkVwYUDYv/mJZK69J7BP7HRUhHEAone7OQHFBRnhQdQ=
github.com/antchfx/htmlquery v1.2.0 h1:oKShnsGlnOHX6t4uj5OHgLKkABcJoqnXpqnscoi9Lpw= github.com/antchfx/htmlquery v1.2.0 h1:oKShnsGlnOHX6t4uj5OHgLKkABcJoqnXpqnscoi9Lpw=
github.com/antchfx/htmlquery v1.2.0/go.mod h1:MS9yksVSQXls00iXkiMqXr0J+umL/AmxXKuP28SUJM8= github.com/antchfx/htmlquery v1.2.0/go.mod h1:MS9yksVSQXls00iXkiMqXr0J+umL/AmxXKuP28SUJM8=
github.com/antchfx/xmlquery v1.2.0 h1:1nrzsSN5mFrlqFWSK9byiq/qXKE7O2vivYzhv1Ksnfw= github.com/antchfx/xmlquery v1.2.0 h1:1nrzsSN5mFrlqFWSK9byiq/qXKE7O2vivYzhv1Ksnfw=
@ -41,6 +43,8 @@ github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc h1:tP7tkU+vI
github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc/go.mod h1:ORH5Qp2bskd9NzSfKqAF7tKfONsEkCarTE5ESr/RVBw= github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc/go.mod h1:ORH5Qp2bskd9NzSfKqAF7tKfONsEkCarTE5ESr/RVBw=
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA= github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA=
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ= github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90 h1:WXb3TSNmHp2vHoCroCIB1foO/yQ36swABL8aOVeDpgg= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90 h1:WXb3TSNmHp2vHoCroCIB1foO/yQ36swABL8aOVeDpgg=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE= github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE=
@ -71,6 +75,8 @@ github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhS
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=

View File

@ -11,6 +11,7 @@ import (
"github.com/velour/catbase/bot/msg" "github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/connectors/discord" "github.com/velour/catbase/connectors/discord"
"github.com/velour/catbase/plugins/gifmeme"
"github.com/velour/catbase/plugins/giphy" "github.com/velour/catbase/plugins/giphy"
"github.com/velour/catbase/plugins/gpt3" "github.com/velour/catbase/plugins/gpt3"
"github.com/velour/catbase/plugins/last" "github.com/velour/catbase/plugins/last"
@ -162,6 +163,7 @@ func main() {
b.AddPlugin(impossible.New(b)) b.AddPlugin(impossible.New(b))
b.AddPlugin(cli.New(b)) b.AddPlugin(cli.New(b))
b.AddPlugin(aoc.New(b)) b.AddPlugin(aoc.New(b))
b.AddPlugin(gifmeme.New(b))
b.AddPlugin(meme.New(b)) b.AddPlugin(meme.New(b))
b.AddPlugin(achievements.New(b)) b.AddPlugin(achievements.New(b))
b.AddPlugin(sms.New(b)) b.AddPlugin(sms.New(b))

136
plugins/gifmeme/gif.go Normal file
View File

@ -0,0 +1,136 @@
package gifmeme
import (
"bytes"
"github.com/andybons/gogif"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/config"
"github.com/velour/catbase/plugins/meme"
"image"
"image/gif"
"net/http"
"time"
)
const tpc = 5
const nColors = 64
func getGif(c *config.Config, s meme.Specification) ([]byte, int, int, error) {
t0 := time.Now()
resp, err := http.Get(s.ImageURL)
if err != nil {
return nil, 0, 0, err
}
g, err := gif.DecodeAll(resp.Body)
if err != nil {
return nil, 0, 0, err
}
log.Debug().Msgf("Len before adding text: %d", len(g.Image))
err = addText(c, g, s.Configs)
log.Debug().Msgf("Len after adding text: %d", len(g.Image))
log.Debug().Msgf("%v elapsed adding text", time.Now().Sub(t0))
if err != nil {
return nil, 0, 0, err
}
out := bytes.NewBuffer([]byte{})
t1 := time.Now()
err = gif.EncodeAll(out, g)
if err != nil {
return nil, 0, 0, err
}
log.Debug().Msgf("%v elapsed encoding gif", time.Now().Sub(t1))
w, h := g.Image[0].Bounds().Max.X, g.Image[0].Bounds().Max.Y
return out.Bytes(), w, h, nil
}
func addText(c *config.Config, g *gif.GIF, text []meme.Text) error {
totalTime := calcTime(g)
framesNeeded, avgFrames := calcTextTime(text)
loops := 1
if totalTime < framesNeeded {
loops = framesNeeded / totalTime
totalTime = loops * totalTime
} else {
framesNeeded = totalTime
}
log.Debug().Msgf("We need %d loops for %d total time with %d frames",
loops, totalTime, framesNeeded)
frames := []*image.Paletted{}
delays := []int{}
idx := 0
toicfg := meme.MkTextOnImageConfig(c)
bounds := g.Image[0].Bounds()
maxSz := max(bounds.Max.X, bounds.Max.Y)
if toicfg.MaxSz > float64(maxSz) {
toicfg.MaxSz = float64(maxSz)
}
currText := 0
currTextFrames := 0
log.Debug().Msgf("Writing text configs %+v on image", text)
log.Debug().Msgf("Starting text with %+v", text[currText])
for i := 0; i < framesNeeded; i++ {
img := g.Image[idx]
i, err := meme.TextOnImage(toicfg, img, []meme.Text{text[currText]})
if err != nil {
log.Error().Err(err).Msg("error encoding gif")
return err
}
frames = append(frames, imgToPaletted(i))
delays = append(delays, g.Delay[idx])
idx = (idx + 1) % len(g.Image)
currTextFrames++
if currTextFrames > avgFrames {
currText = (currText + 1) % len(text)
currTextFrames = 0
log.Debug().Msgf("Switching text to %+v", text[currText])
}
}
g.Image = frames
g.Delay = delays
g.Disposal = nil
g.Config = image.Config{}
return nil
}
func calcTextTime(text []meme.Text) (int, int) {
total := 1
for _, frame := range text {
total += tpc * len(frame.Text)
}
return total, total / len(text)
}
func calcTime(g *gif.GIF) int {
total := 0
for _, frameDelay := range g.Delay {
total += frameDelay
}
return total
}
func imgToPaletted(i image.Image) *image.Paletted {
bounds := i.Bounds()
p := image.NewPaletted(bounds, nil)
quantizer := gogif.MedianCutQuantizer{NumColor: nColors}
quantizer.Quantize(p, bounds, i, image.Point{})
return p
}
func max(x, y int) int {
if x > y {
return x
}
return y
}

106
plugins/gifmeme/gifmeme.go Normal file
View File

@ -0,0 +1,106 @@
package gifmeme
import (
"fmt"
"github.com/jmoiron/sqlx"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/config"
"github.com/velour/catbase/plugins/meme"
"net/url"
"path"
"regexp"
"strings"
)
type gifMap map[string][]byte
// Plugin creates gifs with text
type Plugin struct {
b bot.Bot
c *config.Config
db *sqlx.DB
h bot.HandlerTable
gifs gifMap
}
// New creates a new Plugin
func New(b bot.Bot) *Plugin {
p := &Plugin{
b: b,
c: b.Config(),
db: b.DB(),
gifs: make(gifMap),
}
p.register()
p.registerWeb()
return p
}
func (p *Plugin) register() {
p.h = bot.HandlerTable{
{
Kind: bot.Message, IsCmd: true,
Regex: regexp.MustCompile(`(?i)^gifmeme (?P<gif>\S+) (?P<text>.+)$`),
Handler: p.gifmemeCmd,
},
}
p.b.RegisterTable(p, p.h)
}
func (p *Plugin) gifmemeCmd(r bot.Request) bool {
gifs := p.c.GetMap("gifmeme.memes", map[string]string{
"key": "https://media.giphy.com/media/3o6Zt4HU9uwXmXSAuI/giphy.gif",
})
u, err := url.Parse(gifs[r.Values["gif"]])
if checkErr(p.b, r, err) {
return true
}
texts := strings.Split(r.Values["text"], "||")
configs := []meme.Text{}
// do this till we have configs
for _, t := range texts {
configs = append(configs, meme.Text{
XPerc: 0.5,
YPerc: 0.9,
Text: t,
Caps: true,
})
}
s := meme.Specification{
ImageURL: u.String(),
Configs: configs,
}
gif, w, h, err := getGif(p.c, s)
if checkErr(p.b, r, err) {
return true
}
log.Debug().Msgf("Saving gif %s, len: %d", s.ID(), len(gif))
p.gifs[s.ID()] = gif
u = p.urlForGif(s)
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, u.String())
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "", bot.ImageAttachment{
URL: u.String(),
AltTxt: fmt.Sprintf("%v", texts),
Width: w,
Height: h,
})
return true
}
func (p *Plugin) urlForGif(s meme.Specification) *url.URL {
baseURL := p.c.Get("BaseURL", "")
u, _ := url.Parse(baseURL)
u.Path = path.Join(u.Path, "gifmeme", "api", "gif", s.ID()+".gif")
return u
}
func checkErr(b bot.Bot, r bot.Request, err error) bool {
if err != nil {
msg := fmt.Sprintf("Error: %s", err)
b.Send(r.Conn, bot.Message, r.Msg.Channel, msg)
return true
}
return false
}

37
plugins/gifmeme/web.go Normal file
View File

@ -0,0 +1,37 @@
package gifmeme
import (
"compress/gzip"
"encoding/json"
"fmt"
"github.com/go-chi/chi/v5"
"github.com/gorilla/handlers"
"github.com/rs/zerolog/log"
"net/http"
)
func (p *Plugin) registerWeb() {
r := chi.NewRouter()
r.Handle("/api/gif/{id}.gif", handlers.CompressHandlerLevel(http.HandlerFunc(p.handleGif), gzip.BestCompression))
p.b.RegisterWeb(r, "/gifmeme")
}
func (p *Plugin) handleGif(w http.ResponseWriter, r *http.Request) {
//id := r.URL.Query().Get("id")
id := chi.URLParam(r, "id")
gif, ok := p.gifs[id]
keys := []string{}
for k, _ := range p.gifs {
keys = append(keys, k)
}
log.Debug().Msgf("Looking for %s in gifs: %v", id, keys)
if !ok {
w.WriteHeader(404)
e := struct {
error error
}{fmt.Errorf("%s not found", id)}
jsonErr, _ := json.Marshal(e)
w.Write(jsonErr)
}
w.Write(gif)
}

View File

@ -2,6 +2,7 @@ package meme
import ( import (
"bytes" "bytes"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"image" "image"
@ -25,6 +26,8 @@ import (
"github.com/velour/catbase/config" "github.com/velour/catbase/config"
) )
var fontSizes = []float64{48, 36, 24, 16, 12}
type MemePlugin struct { type MemePlugin struct {
bot bot.Bot bot bot.Bot
c *config.Config c *config.Config
@ -37,7 +40,7 @@ type cachedImage struct {
repr []byte repr []byte
} }
type memeText struct { type Text struct {
XPerc float64 `json:"x"` XPerc float64 `json:"x"`
YPerc float64 `json:"y"` YPerc float64 `json:"y"`
Text string `json:"t",omitempty` Text string `json:"t",omitempty`
@ -45,19 +48,25 @@ type memeText struct {
Font string `json:"f",omitempty` Font string `json:"f",omitempty`
} }
type specification struct { type Specification struct {
ImageURL string ImageURL string
StampURL string StampURL string
Configs []memeText Configs []Text
} }
func (s specification) toJSON() string { func (s Specification) ID() string {
out, _ := json.Marshal(s)
b64 := base64.StdEncoding.EncodeToString(out)
return b64
}
func (s Specification) toJSON() string {
out, _ := json.Marshal(s) out, _ := json.Marshal(s)
return string(out) return string(out)
} }
func SpecFromJSON(input []byte) (specification, error) { func SpecFromJSON(input []byte) (Specification, error) {
out := specification{} out := Specification{}
err := json.Unmarshal(input, &out) err := json.Unmarshal(input, &out)
return out, err return out, err
} }
@ -149,7 +158,7 @@ func (p *MemePlugin) sendMeme(c bot.Connector, channel, channelName, msgID strin
log.Debug().Strs("parts", parts).Msgf("Meme:\n%+v", text) log.Debug().Strs("parts", parts).Msgf("Meme:\n%+v", text)
var config []memeText var config []Text
message = strings.TrimPrefix(message, "`") message = strings.TrimPrefix(message, "`")
message = strings.TrimSuffix(message, "`") message = strings.TrimSuffix(message, "`")
@ -199,7 +208,7 @@ func (p *MemePlugin) sendMeme(c bot.Connector, channel, channelName, msgID strin
stampURL := p.stamp(c, format, from.ID) stampURL := p.stamp(c, format, from.ID)
spec := specification{ spec := Specification{
ImageURL: imgURL, ImageURL: imgURL,
StampURL: stampURL, StampURL: stampURL,
Configs: config, Configs: config,
@ -304,7 +313,7 @@ var defaultFormats = map[string]string{
"raptor": "https://imgflip.com/s/meme/Philosoraptor.jpg", "raptor": "https://imgflip.com/s/meme/Philosoraptor.jpg",
} }
func (p *MemePlugin) findFontSize(config []memeText, fontLocation string, w, h int, sizes []float64) float64 { func findFontSize(config []Text, fontLocation string, w, h int, sizes []float64) float64 {
fontSize := 12.0 fontSize := 12.0
m := gg.NewContext(w, h) m := gg.NewContext(w, h)
@ -341,8 +350,8 @@ func (p *MemePlugin) findFontSize(config []memeText, fontLocation string, w, h i
return fontSize return fontSize
} }
func defaultFormatConfig() []memeText { func defaultFormatConfig() []Text {
return []memeText{ return []Text{
{XPerc: 0.5, YPerc: 0.05, Caps: true}, {XPerc: 0.5, YPerc: 0.05, Caps: true},
{XPerc: 0.5, YPerc: 0.95, Caps: true}, {XPerc: 0.5, YPerc: 0.95, Caps: true},
} }
@ -368,9 +377,7 @@ func (p *MemePlugin) checkMeme(imgURL string) (int, int, error) {
return img.Bounds().Dx(), img.Bounds().Dy(), err return img.Bounds().Dx(), img.Bounds().Dy(), err
} }
func (p *MemePlugin) genMeme(spec specification) ([]byte, error) { func (p *MemePlugin) genMeme(spec Specification) ([]byte, error) {
fontSizes := []float64{48, 36, 24, 16, 12}
jsonSpec := spec.toJSON() jsonSpec := spec.toJSON()
if cached, ok := p.images[jsonSpec]; ok { if cached, ok := p.images[jsonSpec]; ok {
log.Debug().Msgf("Returning cached image for %s", jsonSpec) log.Debug().Msgf("Returning cached image for %s", jsonSpec)
@ -391,28 +398,7 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
return nil, err return nil, err
} }
r := img.Bounds() img, err = TextOnImage(MkTextOnImageConfig(p.c), img, spec.Configs)
w := r.Dx()
h := r.Dy()
maxSz := p.c.GetFloat64("maxImgSz", 750.0)
if w > h {
scale := maxSz / float64(w)
w = int(float64(w) * scale)
h = int(float64(h) * scale)
} else {
scale := maxSz / float64(h)
w = int(float64(w) * scale)
h = int(float64(h) * scale)
}
log.Debug().Msgf("trynig to resize to %v, %v", w, h)
img = resize.Resize(uint(w), uint(h), img, resize.Lanczos3)
r = img.Bounds()
w = r.Dx()
h = r.Dy()
log.Debug().Msgf("resized to %v, %v", w, h)
if spec.StampURL != "" { if spec.StampURL != "" {
img, err = p.applyStamp(img, spec.StampURL) img, err = p.applyStamp(img, spec.StampURL)
@ -424,13 +410,60 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
} }
} }
i := bytes.Buffer{}
png.Encode(&i, img)
p.images[jsonSpec] = &cachedImage{time.Now(), i.Bytes()}
log.Debug().Msgf("Saved to %s\n", jsonSpec)
return p.images[jsonSpec].repr, nil
}
type TextOnImageConfig struct {
MaxSz float64
Font string
}
// MkTextOnImageConfig saves us some db lookups for gif creation
func MkTextOnImageConfig(c *config.Config) TextOnImageConfig {
return TextOnImageConfig{
MaxSz: c.GetFloat64("maxImgSz", 750.0),
Font: c.Get("meme.font", "impact.ttf"),
}
}
func TextOnImage(c TextOnImageConfig, img image.Image, configs []Text) (image.Image, error) {
r := img.Bounds()
w := r.Dx()
h := r.Dy()
maxSz := c.MaxSz
scale := 1.0
if w > h {
scale = maxSz / float64(w)
w = int(float64(w) * scale)
h = int(float64(h) * scale)
} else {
scale = maxSz / float64(h)
w = int(float64(w) * scale)
h = int(float64(h) * scale)
}
if scale > 1.1 || scale < 0.9 {
img = resize.Resize(uint(w), uint(h), img, resize.Lanczos3)
}
r = img.Bounds()
w = r.Dx()
h = r.Dy()
m := gg.NewContext(w, h) m := gg.NewContext(w, h)
m.DrawImage(img, 0, 0) m.DrawImage(img, 0, 0)
defaultFont := p.c.Get("meme.font", "impact.ttf") defaultFont := c.Font
for i, c := range spec.Configs { for i, c := range configs {
if c.Caps { if c.Caps {
spec.Configs[i].Text = strings.ToUpper(c.Text) configs[i].Text = strings.ToUpper(c.Text)
} }
} }
@ -443,12 +476,12 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
if dx*dx+dy*dy >= strokeSize*strokeSize { if dx*dx+dy*dy >= strokeSize*strokeSize {
continue continue
} }
for _, c := range spec.Configs { for _, c := range configs {
fontLocation := c.Font fontLocation := c.Font
if fontLocation == "" { if fontLocation == "" {
fontLocation = defaultFont fontLocation = defaultFont
} }
fontSize := p.findFontSize(spec.Configs, fontLocation, w, h, fontSizes) fontSize := findFontSize(configs, fontLocation, w, h, fontSizes)
m.LoadFontFace(fontLocation, fontSize) m.LoadFontFace(fontLocation, fontSize)
x := float64(w)*c.XPerc + float64(dx) x := float64(w)*c.XPerc + float64(dx)
y := float64(h)*c.YPerc + float64(dy) y := float64(h)*c.YPerc + float64(dy)
@ -459,25 +492,18 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
// Apply white fill // Apply white fill
m.SetHexColor("#FFF") m.SetHexColor("#FFF")
for _, c := range spec.Configs { for _, c := range configs {
fontLocation := c.Font fontLocation := c.Font
if fontLocation == "" { if fontLocation == "" {
fontLocation = defaultFont fontLocation = defaultFont
} }
fontSize := p.findFontSize(spec.Configs, fontLocation, w, h, fontSizes) fontSize := findFontSize(configs, fontLocation, w, h, fontSizes)
m.LoadFontFace(fontLocation, fontSize) m.LoadFontFace(fontLocation, fontSize)
x := float64(w) * c.XPerc x := float64(w) * c.XPerc
y := float64(h) * c.YPerc y := float64(h) * c.YPerc
m.DrawStringAnchored(c.Text, x, y, 0.5, 0.5) m.DrawStringAnchored(c.Text, x, y, 0.5, 0.5)
} }
return m.Image(), nil
i := bytes.Buffer{}
png.Encode(&i, m.Image())
p.images[jsonSpec] = &cachedImage{time.Now(), i.Bytes()}
log.Debug().Msgf("Saved to %s\n", jsonSpec)
return p.images[jsonSpec].repr, nil
} }
func (p *MemePlugin) applyStamp(img image.Image, bullyURL string) (image.Image, error) { func (p *MemePlugin) applyStamp(img image.Image, bullyURL string) (image.Image, error) {