Meet us at PRINT 19. Chicago, IL. Oct. 3 - 5.
This documentation is for the old version. Go to the latest Graphics Mill docs

Loading and Saving Animated GIFs

The GIF file format is the most wide-spread graphic format that supports animation. Animated images saved in this format can be rendered by almost any modern image viewer. Graphics Mill for .NET provides some means to work with animated GIFs.

Two most typical tasks for which Graphics Mill for .NET may be helpful are:

  • Creating a new animated GIF file.
  • Loading an existing animated GIF file and resizing it.

Creating an Animated GIF File

Using Reader and Writer

In order to work with GIF files using Graphics Mill for .NET, you need to know how to deal with its readers and writers. The readers and writers are objects that actually perform reading and writing (respectively) of some media files. There are special readers and writers for each format, for example, for the GIF format they are Aurigma.GraphicsMill.Codecs.GifReader and Aurigma.GraphicsMill.Codecs.GifWriter.

Usage of the reader can be descibed by the following sequence of steps:

  1. Create a new reader instance.
  2. Open the reader.
  3. Load frames from the reader one by one.
  4. Close the reader.

And usage of the writer can be descibed by the following sequence of steps:

  1. Create a new writer instance.
  2. Set its parameters, such as dimensions, optimization type, and so on.
  3. Open the writer.
  4. Add frames to the writer one by one. When adding a frame, you may set for it the GifFrame.Delay and the GifFrame.DisposalMethod property. The first property sets the time to wait before rendering the following frame. The second one specifies what to do after the frame is displayed:
    • The frame can be left in place.
    • Area used by the frame can be restored either to the background or to the previous frame.
    • The decoder is not required to take any action.
  5. Close the writer.

Below is a short example of using a GIF reader and a GIF writer.

Visual Basic
'Create and open a reader and a writer
Dim reader As New Aurigma.GraphicsMill.Codecs.GifReader("D:\wolf.gif")
Dim writer As New Aurigma.GraphicsMill.Codecs.GifWriter("D:\Copy of wolf.gif")

'Set writer properties
writer.BackgroundEntryIndex = reader.BackgroundEntryIndex
writer.GlobalPalette = reader.GlobalPalette
writer.PlaybackCount = reader.PlaybackCount
writer.Optimized = True

For i As Integer = 0 To reader.FrameCount - 1
    'Load a successive frame
    Dim frame As Aurigma.GraphicsMill.Codecs.GifFrame = _
     CType(reader.LoadFrame(i), Aurigma.GraphicsMill.Codecs.GifFrame)

    '...Proccess the frame, if required...

    'Save the frame
    writer.AddFrame(frame)
    frame.Dispose()
Next i

'Close the reader and the writer
writer.Close()
reader.Close()
C#
//Create and open a reader and a writer
Aurigma.GraphicsMill.Codecs.GifReader reader = new
    Aurigma.GraphicsMill.Codecs.GifReader(@"D:\wolf.gif");
Aurigma.GraphicsMill.Codecs.GifWriter writer = new
    Aurigma.GraphicsMill.Codecs.GifWriter(@"D:\Copy of wolf.gif");

//Set writer properties
writer.BackgroundEntryIndex = reader.BackgroundEntryIndex;
writer.GlobalPalette = reader.GlobalPalette;
writer.PlaybackCount = reader.PlaybackCount;
writer.Optimized = true;

for (int i = 0; i <= reader.FrameCount - 1; i++)
{
    //Load a successive frame
    Aurigma.GraphicsMill.Codecs.GifFrame frame =
        (Aurigma.GraphicsMill.Codecs.GifFrame)reader.LoadFrame(i);

    //...Proccess the frame, if required...

    //Save the frame
    writer.AddFrame(frame);
    frame.Dispose();
}
//Close the reader and the writer
writer.Close();
reader.Close();

Optimization

When you create an animated GIF file, most likely, you will want to make it as small by file size as possible while having a sufficient quality. That is when the optimization means of Graphics Mill for .NET come in handy.

Graphics Mill for .NET uses three ways to optimize an animated GIF file:

  • Global palette. A global palette is created for the whole frame sequence. Unless colors in your image change substantially from frame to frame, use an optimized global palette. That will reduce the overall size of the image, as local palettes will not be stored for each frame.
  • Bitmap cropping. If frames following the current one have the same parts of the image, their bitmaps are automatically cropped, if it is possible. This reduces the size of the frame and, consequently, of the whole image.
  • Replacing some pixels with transparent ones. If some pixels in the frames following the current one are the same, those pixels are automatically rendered transparent, if it is possible. This is done to achieve a better compression ratio for bitmaps.

To enable these optimization methods you only need to set the GifWriter.Optimized property to true. However, there is a catch. To build a global palette, Graphics Mill for .NET should load all frames, for which the palette is built, into memory. According to the GIF format specification, no frames will actually be written into a file, until the global palette is created, therefore, the frames will be saved into memory. Naturally, if there are too many frames, the writer will consume too much memory. To avoid this, specify a threshold for the number of frames to build a global palette for. So, if you know that the final image will contain a lot of frames, and they will not introduce new colors, you may also wish to set the GifWriter.BasisOptimizationFrameCount property.

Another way to stop gathering statistics for the global palette is to set the GifFrame.LastFrame to true when you reach the frame where you would like to stop. This may be useful if know that after some frame no more colors will be added. If you set this property, the value of the GifWriter.BasisOptimizationFrameCount property will be ignored.

Note

If frames are added after the global palette is generated, and colors used in those frames are the same as in the global palette, Graphics Mill for .NET will automatically use the global palette. However, if at least one color will not be in the global palette, the frame will be saved with a local palette.

Here is an example of creating an optimized sequence of frames.

Visual Basic
Dim folder As String = "C:\Documents and Settings\All Users\Documents\My " & _
 "Pictures\Sample Pictures\"
Dim images() As String = New String() {"Blue hills.jpg", "Sunset.jpg", "Water lilies.jpg", "Winter.jpg"}
Dim bitmap As New Aurigma.GraphicsMill.Bitmap()
Dim writer As New Aurigma.GraphicsMill.Codecs.GifWriter("D:\slideshow.gif")

'Use optimization
writer.Optimized = True

For Each image As String In images
    bitmap.Load(folder & image)

    'Convert the bitmap to an indexed format
    bitmap.ColorManagement.ConvertToIndexed(8, _
     Aurigma.GraphicsMill.ColorPaletteType.Adaptive, Nothing)

    'Save the frame
    Dim frame As New Aurigma.GraphicsMill.Codecs.GifFrame
    frame.Delay = 100
    frame.SetBitmap(bitmap)
    writer.AddFrame(frame)
    frame.Dispose()
Next image

writer.Dispose()
bitmap.Dispose()
C#
string folder = @"C:\Documents and Settings\All Users\Documents\My " +
    @"Pictures\Sample Pictures";
string[] images = {"Blue hills.jpg", "Sunset.jpg", "Water lilies.jpg",
    "Winter.jpg"};
Aurigma.GraphicsMill.Bitmap bitmap = new Aurigma.GraphicsMill.Bitmap();
Aurigma.GraphicsMill.Codecs.GifWriter writer = new
    Aurigma.GraphicsMill.Codecs.GifWriter(@"D:\slideshow.gif");

//Use optimization
writer.Optimized = true;

foreach (string image in images)
{
    bitmap.Load(folder + image);

    //Convert the bitmap to an indexed format
    bitmap.ColorManagement.ConvertToIndexed(8,
        Aurigma.GraphicsMill.ColorPaletteType.Adaptive, null);

    //Save the frame
    Aurigma.GraphicsMill.Codecs.GifFrame frame = new
        Aurigma.GraphicsMill.Codecs.GifFrame();
    frame.Delay = 100;
    frame.SetBitmap(bitmap);
    writer.AddFrame(frame);
    frame.Dispose();
}
writer.Dispose();
bitmap.Dispose();

Resizing an Animated GIF File

Suppose, you need to resize an existing animated GIF file. To do that you will need to read the image frame by frame, and while for each frame perform the following:

  1. Resize the frame in a high quality mode. That means that the bitmap will be converted from an indexed pixel format to an RGB format with an alpha channel. This way, no colors will be lost, and transparency of the bitmap will be preserved. The transparency can be present, even if the image itself does not seem to have transparent areas. That happens because animated GIFs are usually optimized, and if they contain areas that do not change from frame to frame, these areas are rendered transparent.
  2. Convert the bitmap back to the indexed format keeping the alpha channel values. For that you will need to set the ColorManagementProvider.PaletteAlphaThreshold property for your bitmap. This way the pixels under the threshold will become completely transparent, and pixels above it—completely opaque.
  3. Calculate new coordinates of the left top point of the frame. Note that if some frames of the original image have non-zero coordinates, after resize they may "twitch" a little. That happens if after resize such frames have fractional coordinates. The GIF format does not support fractional coordinates, and rounding may lead to loss of precision.

Here is an example implementation of the above algorithm that scales down an animated GIF.

Visual Basic
Dim reader As New Aurigma.GraphicsMill.Codecs.GifReader("D:\wolf.gif")
Dim writer As New Aurigma.GraphicsMill.Codecs.GifWriter("D:\big_wolf.gif")

writer.Optimized = True

'Copy general properties of the source file
writer.BackgroundEntryIndex = reader.BackgroundEntryIndex
writer.GlobalPalette = reader.GlobalPalette
writer.PlaybackCount = reader.PlaybackCount

Dim bitmap As New Aurigma.GraphicsMill.Bitmap()

For i As Integer = 0 To reader.FrameCount - 1
    'Read a successive frame
    Dim frame As Aurigma.GraphicsMill.Codecs.GifFrame = _
     CType(reader.LoadFrame(i), Aurigma.GraphicsMill.Codecs.GifFrame)
    frame.GetBitmap(bitmap)

    'Preserve the original palette
    Dim palette As Aurigma.GraphicsMill.ColorPalette = bitmap.Palette

    'Resize the bitmap in a high quality mode
    bitmap.Transforms.Resize(frame.Width \ 2, frame.Height \ 2, _
     Aurigma.GraphicsMill.Transforms.ResizeMode.Resize, _
     Aurigma.GraphicsMill.Transforms.InterpolationMode.HighQuality)

    'Return to the indexed format
    bitmap.ColorManagement.PaletteAlphaThreshold = 127
    bitmap.ColorManagement.ConvertToIndexed(frame.BitsPerPixel, _
     Aurigma.GraphicsMill.ColorPaletteType.Custom, palette)

    'Save the frame
    frame.SetBitmap(bitmap)
    frame.Top = frame.Top \ 2
    frame.Left = frame.Left \ 2
    writer.AddFrame(frame)
    frame.Dispose()
Next i

writer.Dispose()
reader.Dispose()
bitmap.Dispose()
C#
Aurigma.GraphicsMill.Codecs.GifReader reader = new
    Aurigma.GraphicsMill.Codecs.GifReader(@"D:\wolf.gif");
Aurigma.GraphicsMill.Codecs.GifWriter writer = new
    Aurigma.GraphicsMill.Codecs.GifWriter(@"D:\big_wolf.gif");

writer.Optimized = true;

//Copy general properties of the source file
writer.BackgroundEntryIndex = reader.BackgroundEntryIndex;
writer.GlobalPalette = reader.GlobalPalette;
writer.PlaybackCount = reader.PlaybackCount;

Aurigma.GraphicsMill.Bitmap bitmap = new Aurigma.GraphicsMill.Bitmap();

for (int i = 0; i <= reader.FrameCount - 1; i++)
{
    //Read a successive frame
    Aurigma.GraphicsMill.Codecs.GifFrame frame =
        (Aurigma.GraphicsMill.Codecs.GifFrame)reader.LoadFrame(i);
    frame.GetBitmap(bitmap);

    //Preserve the original palette
    Aurigma.GraphicsMill.ColorPalette palette = bitmap.Palette;

    //Resize the bitmap in a high quality mode
    bitmap.Transforms.Resize(frame.Width / 2, frame.Height / 2,
        Aurigma.GraphicsMill.Transforms.ResizeMode.Resize,
        Aurigma.GraphicsMill.Transforms.InterpolationMode.HighQuality);

    //Return to the indexed format
    bitmap.ColorManagement.PaletteAlphaThreshold = 127;
    bitmap.ColorManagement.ConvertToIndexed(frame.BitsPerPixel,
        Aurigma.GraphicsMill.ColorPaletteType.Custom, palette);

    //Save the frame
    frame.SetBitmap(bitmap);
    frame.Top = frame.Top / 2;
    frame.Left = frame.Left / 2;
    writer.AddFrame(frame);
    frame.Dispose();
}
writer.Dispose();
reader.Dispose();
bitmap.Dispose();

See Also

Reference

Manual