6

I'm trying to write some code to export animated .gifs from a WPF application using GifBitmapEncoder. What I have so far works fine but when I view the resulting .gif it only runs once and then stops - I'd like to have it looping indefinitely.

I've found this previous similar question:

How do I make a GIF repeat in loop when generating with BitmapEncoder

However, he is using the BitmapEncoder from Windows.Graphics.Imaging rather than the Windows.Media.Imaging version, which seems to be a bit different. Nonetheless, that gave me a direction and after a bit more googling I came up with this:

Dim encoder As New GifBitmapEncoder
Dim metaData As New BitmapMetadata("gif")
metaData.SetQuery("/appext/Application", System.Text.Encoding.ASCII.GetBytes("NETSCAPE2.0"))
metaData.SetQuery("/appext/Data", New Byte() {3, 1, 0, 0, 0})

'The following line throws the exception "The designated BitmapEncoder does not support global metadata.":
'encoder.Metadata = metaData

If DrawingManager.Instance.SelectedFacing IsNot Nothing Then
   For Each Frame As Frame In DrawingManager.Instance.SelectedFacing.Frames
       Dim bmpFrame As BitmapFrame = BitmapFrame.Create(Frame.CombinedImage, Nothing, metaData, Nothing)
       encoder.Frames.Add(bmpFrame)
   Next
End If

Dim fs As New FileStream(newFileName, FileMode.Create)
encoder.Save(fs)
fs.Close()

Initially I tried adding the metadata directly to the encoder (as in the commented-out line in the code above), but at runtime that throws the exception "The designated BitmapEncoder does not support global metadata". I can instead attach my metadata to each frame, but although that doesn't crash it the resultant .gif doesn't loop either (and I would expect that the looping metadata would need to be global anyway).

Can anyone offer any advice?

Community
  • 1
  • 1
Paul Jeffries
  • 95
  • 2
  • 6
  • Why don't you use Windows.Graphics.Imaging? – Robert Levy Sep 10 '13 at 12:50
  • Sorry, I should have said - Windows.Graphics.Imaging appears to be usable only in a Windows 8 environment and that's no good for me. Even were that not the case, I'd ideally like to avoid adding any dependencies to the project that I don't have to. – Paul Jeffries Sep 10 '13 at 18:50
  • According to MSDN: "Graphics Interchange Format (GIF) images do not support global preview, global thumbnails, **global metadata**, frame level thumbnails, or frame level metadata." – Scott Solmer Sep 10 '13 at 20:23
  • Thanks for pointing that out, Okuma Scott. I suspect what they actually mean by that though is that *GifBitmapEncoder* doesn't support those things - the GIF format itself appears to. Strangely enough GifBitmap*Decoder* supports at least frame level metadata because I've come across examples of people reading it to determine things like frame playback delay... In any case, perhaps I'm barking up the wrong tree with GifBitmapEncoder and need to come at it in a different way. – Paul Jeffries Sep 11 '13 at 21:09
  • 1
    did you ever find an adequate solution? – friartuck Jan 04 '15 at 10:05
  • 1
    @Prof: I'm afraid not, and as it was for a non-essential part of a hobby project I let it slide. My two best options seemed to either be to abandon GifBitmapEncoder and roll my own .gif writer or to use it to write out the gif and then afterwards insert the appropriate application extension into the file. I'm still interested in finding an easy way to do it so if you manage to find something please let me know! – Paul Jeffries Jan 05 '15 at 17:55

2 Answers2

7

I finally got this to work after studying this article and referencing the raw bytes of GIF files. If you want to do so yourself, you can get the bytes in hex format using PowerShell like so...

$bytes = [System.IO.File]::ReadAllBytes("C:\Users\Me\Desktop\SomeGif.gif")
[System.BitConverter]::ToString($bytes)

The GifBitmapEncoder appears to write the Header, Logical Screen Descriptor, then the Graphics Control Extension. The "NETSCAPE2.0" extension is missing. In GIFs from other sources that do loop, the missing extension always appears right before the Graphics Control Extension.

So I just plugged in the bytes after the 13th byte, since the first two sections are always this long.

        // After adding all frames to gifEncoder (the GifBitmapEncoder)...
        using (var ms = new MemoryStream())
        {
            gifEncoder.Save(ms);
            var fileBytes = ms.ToArray();
            // This is the NETSCAPE2.0 Application Extension.
            var applicationExtension = new byte[] { 33, 255, 11, 78, 69, 84, 83, 67, 65, 80, 69, 50, 46, 48, 3, 1, 0, 0, 0 };
            var newBytes = new List<byte>();
            newBytes.AddRange(fileBytes.Take(13));
            newBytes.AddRange(applicationExtension);
            newBytes.AddRange(fileBytes.Skip(13));
            File.WriteAllBytes(saveFile, newBytes.ToArray());
        }
Jared
  • 764
  • 7
  • 17
-1

Did you know that you can just download this functionality? Take a look at the WPF Animated GIF page on CodePlex. Alternatively, there is WPF Animated GIF 1.4.4 on Nuget Gallery. If you prefer a tutorial, then take a look at the GIF Animation in WPF page on the Code Project website.

@PaulJeffries, I do apologise... I misunderstood your question. I have used some code from a post here before to animate a .gif file. It is quite straight forward and you might be able to 'reverse engineer' it for your purposes. Please take a look at the How do I get an animated gif to work in WPF? post to see if that helps. (I am aware that the code's actual purpose is also to animate a .gif).

Community
  • 1
  • 1
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • 1
    Thanks Sheridan, unfortunately however those pages only show the opposite of what I'm trying to do! I'm not trying to display a looping gif in a WPF application, I'm trying to export a looping gif *from* a WPF application, so that when it is viewed elsewhere - in a web browser, for example - it loops automatically. – Paul Jeffries Sep 10 '13 at 18:43