Skip to content

Instantly share code, notes, and snippets.

@henkman
Created January 16, 2023 21:14
Show Gist options
  • Save henkman/9ef276ebcb60b81cbf59d7995288f07a to your computer and use it in GitHub Desktop.
Save henkman/9ef276ebcb60b81cbf59d7995288f07a to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"fmt"
"image"
"image/draw"
"image/gif"
"image/png"
"io"
"os"
"path/filepath"
)
func main() {
const GIF = `some.gif`
fd, err := os.Open(GIF)
if err != nil {
panic(err)
}
defer fd.Close()
frames, err := GetAnimatedGifFrames(bufio.NewReader(fd))
if err != nil {
panic(err)
}
i := 0
for frame := range frames {
fp := filepath.Join("img", fmt.Sprintf("%03d.png", i))
{
fd, err := os.OpenFile(fp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0750)
if err != nil {
panic(err)
}
bout := bufio.NewWriter(fd)
png.Encode(bout, frame)
bout.Flush()
fd.Close()
}
i++
}
}
func GetAnimatedGifFrames(in io.Reader) (<-chan *image.RGBA, error) {
g, err := gif.DecodeAll(in)
if err != nil {
return nil, err
}
imgchan := make(chan *image.RGBA)
go func() {
rect := g.Image[0].Rect
dst := image.NewRGBA(rect)
noDisposeIndex := -1
draw.Draw(dst, rect, g.Image[0], image.ZP, draw.Src)
imgchan <- dst
for index := 1; index < len(g.Image); index++ {
switch g.Disposal[index-1] {
case gif.DisposalNone:
draw.Draw(dst, rect, g.Image[index], image.ZP, draw.Over)
noDisposeIndex = index - 1
case gif.DisposalBackground:
draw.Draw(dst, rect, g.Image[index], image.ZP, draw.Src)
case gif.DisposalPrevious:
if noDisposeIndex >= 0 {
draw.Draw(dst, rect, g.Image[noDisposeIndex], image.ZP, draw.Src)
draw.Draw(dst, rect, g.Image[index], image.ZP, draw.Over)
} else {
draw.Draw(dst, rect, g.Image[index], image.ZP, draw.Src)
}
default:
draw.Draw(dst, rect, g.Image[index], image.ZP, draw.Over)
}
imgchan <- dst
}
close(imgchan)
}()
return imgchan, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment