1

In a UI library for resource constrained embedded devices I would like to create an optimized GIF decoder. The goal is to store the decoded image in indexed format. To do so the GIF needs to have only a global palette and no local palettes on each frame. (With local palettes a completely different palette can be used if only smaller areas of the GIF is updated on a frame.)

So the question is: how can I convert a GIF file to use only a global palette. I know that it can result in color loss, but it's an acceptable trade off.

1 Answers1

0

This library (disclaimer: the author is me) allows you to re-save an animated GIF with just a single palette.

1. Specific solution for System.Drawing.Bitmap

If you use GDI+ on Windows, then the simplest solution is to use the dedicated package because it has higher-level methods that make the conversion really simple:

// obtain the original GIF with possibly per-frame local palette
using var myOriginalGif = new Bitmap(thePathForMyHighColorAnimatedGif);

// extracting the frames
Bitmap[] frames = myOriginalGif.ExtractBitmaps();

// save a new animated GIF with a fix palette
frames.SaveAsAnimatedGif(outputPath,
    delay: TimeSpan.FromMilliseconds(100), // now using a constant delay
    quantizer: PredefinedColorsQuantizer.SystemDefault8BppPalette(), // the web-safe palette
    ditherer: OrderedDitherer.Bayer8x8); // optional

Of course, the example above has a disadvantage that ExtractBitmaps does not retrieve the delays information between the frames so you need to come up with some custom delay that you can pass to the SaveAsAnimatedGif overloads.

The key for your goal is the quantizer parameter. In the example I used the SystemDefault8BppPalette, which uses the standard web-safe palette but you can use any other custom fix palette by the FromCustomPalette method. If you don't specify this parameter, then an OptimizedPaletteQuantizer instance will be used that allows creating a local palette for each frames.

The ditherer argument is optional, it just helps preserving the details better. In the example I used an OrderedDitherer but for the best quality you can use an ErrorDiffusionDitherer, though it's a bit slower.

2. General solution for any other environment

If you can't (or don't want to) use System.Drawing.Bitmap, then you can use your favorite environment that has a GIF decoder to obtain the frames. To be able to use my GifEncoder class from the technology-independent core package you need to access the frames as IReadableBitmapData instances.

  • If you use WPF/UWP/WinUI/SkiaSharp, then you can do it easily by by using one of the environment-specific packages: you only need to use the corresponding GetReadableBitmapData extension for the specific bitmap type (eg. WritableBitmap or SkiaSharp's SKBitmap, etc.).
  • If you use any other environment you can use the BitmapDataFactory class to obtain a bitmap data for the frames. For example, here is how you can do it for ImageSharp bitmaps.

And once you have the frames then you can simply use the EncodeAnimation where the configuration's Quantizer should be set as described at solution 1.

3. Tool-based solution

If you don't want to convert your GIF programmatically, then you can use Nicke Manarin's excellent tool, ScreenToGif, which also contains my GIF encoder. This is the simplest solution if want to preserve the original delays for the frames.

Just load the original GIF in the Editor, click on Save As, and select the GIF format and KGy SOFT encoder. In fact, it has a predefined 'Low quality' preset, which uses the same fixed web-safe palette and ordered dithering demonstrated in solution 1.

ScreenToGif KGy SOFT GIF Encoder

A possible example:

Original high color GIF Conversion result with web-safe palette and ordered dithering
High color GIF animation Converted GIF animation with a global palette
György Kőszeg
  • 17,093
  • 6
  • 37
  • 65