Skip to content

Instantly share code, notes, and snippets.

@Deleplace
Last active June 20, 2023 15:29
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Deleplace/240675c1a70e7df4cd5b04aa204aeeba to your computer and use it in GitHub Desktop.
Save Deleplace/240675c1a70e7df4cd5b04aa204aeeba to your computer and use it in GitHub Desktop.
A web server that generates dancing gopher GIFs
# Copyright 2019 Google LLC
# SPDX-License-Identifier: Apache-2.0
package main
import (
"fmt"
"image"
"image/color"
"image/draw"
"image/gif"
"log"
"net/http"
"strconv"
"strings"
"golang.org/x/image/font"
"golang.org/x/image/font/basicfont"
"golang.org/x/image/math/fixed"
)
const sourceGifURL = "https://storage.googleapis.com/deleplace-sandbox/2019/go-gcf/gopher-dance-long-3x-sign.gif"
func main() {
http.HandleFunc("/", MakeGif)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func MakeGif(w http.ResponseWriter, r *http.Request) {
signText := "foobar"
if t := r.FormValue("text"); t != "" {
fmt.Println("Custom text", t)
signText = t
}
var newLightColor color.Color = color.RGBA{255, 200, 200, 255}
if color := r.FormValue("color"); color != "" {
fmt.Println("Custom color", color)
var err error
newLightColor, err = decodeHexColor(color)
check(err)
}
newDimmedColor := dim(newLightColor)
resp, err := http.Get(sourceGifURL)
check(err)
g, err := gif.DecodeAll(resp.Body)
check(err)
resp.Body.Close()
// fmt.Println(len(g.Image), "frames")
textColor := color.RGBA{240, 240, 240, 255}
for i, img := range g.Image {
modify(i, img, newLightColor, newDimmedColor, signText, textColor)
}
w.Header().Set("Content-Type", "image/gif")
w.Header().Set("Cache-Control", "no-cache, no-store, no-transform")
err = gif.EncodeAll(w, g)
check(err)
log.Println("Done.")
}
func modify(i int, img *image.Paletted, newLightColor, newDimmedColor color.Color, message string, textColor color.Color) {
for j, c := range img.Palette {
if c == originalLightColor {
// Main light blue
img.Palette[j] = newLightColor
} else if c == originalDimmedColor {
// Secondary dimmed blue
img.Palette[j] = newDimmedColor
}
}
addLabel(img, x[23-i], y[23-i], message, textColor)
}
func addLabel(img draw.Image, x, y int, label string, col color.Color) {
fmt.Println("addLabel")
words := strings.Split(label, " ")
m := len(words)
dy := 10 + (76 / 2) - ((m * 13) / 2)
for _, word := range words {
n := len(word)
dx := 4 + (100 / 2) - ((n * 7) / 2)
point := fixed.Point26_6{
fixed.Int26_6((dx + x) * 64),
fixed.Int26_6((dy + y) * 64),
}
face := basicfont.Face7x13
d := &font.Drawer{
Dst: img,
Src: image.NewUniform(col),
Face: face,
Dot: point,
}
d.DrawString(word)
dy += 13
}
}
func decodeHexColor(s string) (color.Color, error) {
if len(s) != 6 {
return nil, fmt.Errorf("Only accepting color in hex RRGGBB format")
}
var v [3]int64
var err error
for i := 0; i < 3; i++ {
sub := s[2*i : 2+2*i]
v[i], err = strconv.ParseInt(sub, 16, 0)
if err != nil {
return nil, err
}
}
return color.RGBA{uint8(v[0]), uint8(v[1]), uint8(v[2]), 0xff}, nil
}
func dim(c color.Color) color.Color {
rr, gg, bb, aa := c.RGBA()
r, g, b, a := uint8(rr), uint8(gg), uint8(bb), uint8(aa)
d := uint8(25)
if r > d {
r -= d
} else {
r = 0
}
if g > d {
g -= d
} else {
g = 0
}
if b > d {
b -= d
} else {
b = 0
}
return color.RGBA{r, g, b, a}
}
// Hardcoded positions of the sign
var x = []int{45, 72, 76, 70, 43, 14, 18, 27, 48, 76, 78, 73, 44, 14, 11, 25, 46, 76, 82, 74, 42, 14, 11, 27}
var y = []int{101, 116, 117, 90, 99, 100, 114, 100, 96, 114, 117, 91, 97, 99, 105, 93, 96, 110, 117, 90, 97, 97, 109, 98}
var originalLightColor = color.RGBA{0x96, 0xd6, 0xff, 0xff}
var originalDimmedColor = color.RGBA{0x8d, 0xc9, 0xf0, 0xff}
func check(err error) {
if err != nil {
panic(err)
}
}
@zLeki
Copy link

zLeki commented Apr 3, 2022

cool

@gospacedev
Copy link

Very cool!

@MarcusXavierr
Copy link

Amazing! Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment