7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Go3Advent Calendar 2019

Day 23

Go を用いて猫 GIF に LGTM テキストをつけたかった

Posted at

最初に

35f52309-0054-4794-880b-52e0be6612c0.gif

上記が font.Drawer を用いて GIF に対しLGTMを付与した例です :sob:
まぁこれはこれでアリかなって思っちゃいますが、LGTM が躍動感出て不具合感が否めません。

※ 文字に被らない自覚ある猫の場合は以下の通りです。
813c4da8-9735-42fe-9142-f9b8cf819c15.gif

原因

残像が残るのは Gif クラスの Disposal が原因と考えられる。
Go で設定出来る Disposal Method は3種類あり、

DisposalNone       = 0x01
DisposalBackground = 0x02
DisposalPrevious   = 0x03

取得元の GIF にはドキュメント通りデフォルトの DisposalNone が設定されていたので、
DisposalBackground に変更すれば、残像が残らないのではと考えたが、
DisposalNone から DisposalBackground に Disposal を指定し直しても描画崩れは発生していた。

※ Disposal の詳細は以下のページに詳しく解説されています。
http://www.snap-tck.com/room03/c02/cg/cg04_02.html

残像が残ってしまうソースコード

func main() {
	f, err := os.Open("hoge.gif")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	
	g, err := gif.DecodeAll(f)
	if err != nil {
		panic(err)
	}

	for _, img := range g.Image {
		if err = addText(img, "LGTM"); err != nil {
			panic(err)
		}
	}

	newFile, err := os.Create("edited_hoge.gif")
	if err != nil {
		return "", err
	}
	defer newFile.Close()

	if err := gif.EncodeAll(newFile, g); err != nil {
		return "", err
	}
}

// addText は img に対して text を中央下部に描画する
func addText(img *image.Paletted, text string) error {
	tt, err := truetype.Parse(gobold.TTF)
	if err != nil {
		return err
	}
	
	d := &font.Drawer{
		Dst:  img,
		Src:  image.NewUniform(color.White),
		Face: truetype.NewFace(tt, &truetype.Options{
			Size:40.0,
		}),
		Dot:  fixed.Point26_6{
			fixed.Int26_6(((img.Rect.Dx()/2) - 60) * 64),
			fixed.Int26_6((img.Rect.Dy()-20) * 64),
		},
	}
	d.DrawString(text)
	return nil
}

解決方法

font.Drawer を用いるのではなく、
LGTM 文字列のみの画像を動的に作成し、GIF に合成 する方法で残像の問題は解決しました。

tmp.gif

残像がないソースコード

func main() {
	f, err := os.Open("hoge.gif")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	
	g, err := gif.DecodeAll(f)
	if err != nil {
		panic(err)
	}

	lgtmImage, err := generateLGTMImage(g.Image[0]);
	if err != nil {
		panic(err)
	}

	var images []*image.Paletted
	var delays []int
	var disposals []byte

	for i, img := range g.Image {
		logoRectangle := image.Rectangle{image.Point{0, 0}, lgtmImage.Bounds().Size()}
		draw.Draw(img, logoRectangle, lgtmImage, image.Point{0, 0}, draw.Over)
		images = append(images, img)
		delays = append(delays, g.Delay[i])
		disposals = append(disposals, gif.DisposalNone)
	}

	buf := new(bytes.Buffer)
	if err = gif.EncodeAll(buf, &gif.GIF{
		Image:           images,
		Delay:           delays,
		Disposal:        disposals,
		BackgroundIndex: g.BackgroundIndex,
		Config: g.Config,
	}); err != nil {
		panic(err)
	}

	return buf.Bytes(), nil
}

func generateLGTMImage(img *image.Paletted) (image.Image, error) {
	// gif のサイズに合わせて img を生成
	newImg := image.NewRGBA(img.Rect)
	tt, err := truetype.Parse(gobold.TTF)
	if err != nil {
		return nil, err
	}

	d := &font.Drawer{
		Dst:  newImg,
		Src:  image.NewUniform(color.White),
		Face: truetype.NewFace(tt, &truetype.Options{
				Size:40,
			},
		),
		Dot:  fixed.Point26_6{fixed.Int26_6(((newImg.Rect.Dx()/2) - 60) * 64), fixed.Int26_6((newImg.Rect.Dy()-20) * 64)},
	}
	d.DrawString("LGTM")
	return newImg, nil
}

感想

この問題にハマった時に font.Drawer でどうにかならないか試行錯誤してましたが、
別の方法である「合成」で問題解消出来たのでよかったです。

これで猫 gif で心置きなく LGTM 出来ます!
以下のサイトから使ってもいいですし、自分のオリジナルの LGTM gif でも活用してください。

余談

LGTM Cat は Go + Firestore + Cloud Run で作成してます。
元々は Node.JS + GAE (F1 1台)で運用していたのですが、gif を並べて表示しているのでメモリが足りずによくサーバーが落ちてしまっていました。
その点 Cloud Run は無料枠が多い且つ、メモリも好きなだけ増やせれるので上記の問題を解決出来ました。

7
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?