Provide a service for animated gif creation

Animated GIF Service

This example will provide a HTTP service that creates animated GIF containing the text that is passed as input to the service.

The service uses the standard library of Go and some additional libraries:

What we get is a service that responds to requests like these
http://localhost:8000/animated.gif?name=Max
generating images like this

Create the GIF Image

The package image/gif provides all the methods and structures to operate with GIF image format.

// Generate an animated gif
func GenerateAnimation(text string, fontfile string, out io.Writer) {
    const (
        nframes = 64 // number of animation frames
        delay =8 // delay between frames in 10ms units
    )
    var xsize int = 40 + 30 * len(text)
    var ysize int = 200
    //load the font ...
    var palette = make([]color.Color,0)
    // generate palette
    for i := 0; i < nframes; i++ {
        palette = append(palette, color.RGBA{R:0,G:0,B:uint8(4*i), A:255})
    }
    anim := gif.GIF{LoopCount: nframes}
    for i := 0; i < nframes; i++ {
        rect := image.Rect(0, 0, xsize, ysize)
        img := image.NewPaletted(rect, palette)
        for x := 0; x < xsize; x++ {
            for y := 0; y < ysize; y++ {
                img.SetColorIndex(x, y, uint8(i))
            }
        }
        WriteText(text, f, img)
        anim.Delay = append(anim.Delay, delay)
        anim.Image = append(anim.Image, img)
    }
    gif.EncodeAll(out, &anim)
}
The function GenerateAnimation first creates the palette and then performs a loop to add the images that compose the animation assigning an identical delay to every frame.
The function WriteText is called before adding the image to the GIF.

Add the text to the image

First load the TrueType Font, this is done in the function GenerateAnimation

    // Read the font data.
    fontBytes, err := ioutil.ReadFile(fontfile)
    if err != nil {
        log.Println(err)
        return
    }
    f, err := freetype.ParseFont(fontBytes)
    if err != nil {
        log.Println(err)
        return
    }

The font is passed to the method that writes the text
// Write a text inside the image
func WriteText(text string, f *truetype.Font, img *image.Paletted){
    // Initialize the context.
    fg:= image.Black
    c := freetype.NewContext()
    c.SetDPI(72)
    c.SetFont(f)
    c.SetFontSize(64)
    c.SetClip(img.Bounds())
    c.SetDst(img)
    c.SetSrc(fg)
    // Draw the text.
    pt := freetype.Pt(40, 120)
    c.DrawString(text, pt) }
The freetype library writes the text on the destination image by creating a mask from the source image. In this example, the source image is a black foreground and the destination image is a frame of the animated GIF.

Activate the HTTP service

Now we will create an HTTP service that reads the parameter name from the querystring and writes the text in the animated GIF.
Using Gin-Gonic for service activation requires a few lines of code

func main(){
    router := gin.New()
    // Global middleware
    router.Use(gin.Logger())
    router.Use(gin.Recovery())
    router.GET("/animated.gif", animatedgif)
    router.Run("0.0.0.0:8000")
}
URI handler for /animated.gif simply calls the function GenerateAnimation
func animatedgif (c *gin.Context) {
    name := c.Query("name")
   GenerateAnimation("Hello " + name + " !", "luxisr.ttf", c.Writer) }

Conclusions


This simple service lends itself to be used in many ways. Actual use cases can be:
  • generate dynamic banners for websites
  • insert dynamic images in email like animated welcome messages, produced when the message is opened

The service can be modified to produce a counter or a reverse counter to be used also on websites and in messages or, with some more changes, the service can be easy modified to produce a lottery service (the first that displays the banner read "You have win", the next read "You lost, try again").


The full example code can be downloaded here


Commenti

Post popolari in questo blog

OAuth 2.0 Server & Authorization Middleware for Gin-Gonic

From the database schema to RESTful API with DinGo

OVO Key/Value Storage