Modifying a gif image in go…not setting transparency correctly

Issue

I have some code which performs the following logical operations:

  • Read in and decode a gif image to a *GIF using gif.DecodeAll
  • Modify some pixels in each frame of the *GIF using image.Set
  • Write out the resulting modified image using gif.EncodeAll

Here’s some code snippets to help demonstrate what the code is doing (error handling, file closing, etc removed for brevity):

f, err := os.Open(filename)
reader := bufio.NewReader(f)
g, err := gif.DecodeAll(reader)
err = modify_image(g)

of, err := os.Create("out.gif")
writer := bufio.NewWriter(of)
err = gif.EncodeAll(writer, g)

Here’s the modify_image function:

func modify_image(img *gif.GIF) error {
    for i := 0; i < len(img.Image); i++ {
        err := modify_frame(img.Image[i])
    }
    return nil
}

And modify_frame:

func modify_frame(frame *image.Paletted) error {
    xmin := frame.Rect.Min.X
    ymin := frame.Rect.Min.Y
    xmax := frame.Rect.Max.X
    ymax := frame.Rect.Max.Y

    for y := ymin; y < ymax; y++ {
        for x := xmin; x < xmax; x++ {
            if should_turn_pixel_transparent(frame, x, y) {
                frame.Set(x, y, color.RGBA64{0, 0, 0, 0})
            }
        }
    }
    return nil
}

The out.gif that this code produces has the correct pixels turned transparent, but as the animation proceeds, the pixels which I turned transparent are not “clearing”; i.e. as these transparent pixels are written over non-transparent pixels, the non-transparent pixels underneath are still displayed.

My (brief) understanding is that there are two different methods for representing transparency in gifs. I don’t know if I need to use index transparency versus alpha transparency, or if I’m just doing things entirely wrong. Any advice would be appreciated.

Solution

This is often omitted or not covered in various golang tutorials for generating gifs, but along with setting the delay Delay slice for each frame in the Image slice, it is also optional to set Disposal for each frame of the gif. DisposalNone is used of the slice does not have a member corresponding to the current frame index.

Disposal options are:

const (
    DisposalNone       = 0x01 // dont dispose of previous frames
    DisposalBackground = 0x02 // dispose of specific colour in previous frames defined by GIF.BackgroundIndex
    DisposalPrevious   = 0x03 // dispose of the previous frame
)

The following is the resulting gif for each type of disposal.

DisposalNone:
gif.DisposalNone

DisposalBackground:
gif.DisposalBackground

DisposalPrevious:
gif.DisposalPrevious

Answered By – Schmorrison

Answer Checked By – Terry (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.