catbase/plugins/gifmeme/gif.go

137 lines
3.0 KiB
Go

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
}