CGIF, A fast and lightweight GIF encoder that can create GIF animations and images

Overview

CGIF, a GIF encoder written in C

A fast and lightweight GIF encoder that can create GIF animations and images. Summary of the main features:

  • user-defined global or local color-palette with up to 256 colors (limit of the GIF format)
  • size-optimizations for GIF animations:
    • option to set a pixel to transparent if it has identical color in the previous frame (transparency optimization)
    • do encoding just for the rectangular area that differs from the previous frame (width/height optimization)
  • fast: a GIF with 256 colors and 1024x1024 pixels can be created in below 50 ms even on a minimalistic system
  • MIT license (permissive)
  • different options for GIF animations: static image, N repetitions, infinite repetitions
  • additional source-code for verifying the encoder after making changes
  • user-defined delay time from one frame to the next (can be set independently for each frame)
  • source-code conforms to the C99 standard

Examples

To get started, we suggest that you have a look at our code examples. cgif_example_video.c is an example that creates a GIF animation. cgif_example.c is an example for a static GIF image.

Overview

To get an overview of the code, we recommend having a look at the header file cgif.h where types and functions are declared. The corresponding implementations can be found in cgif.c. Here the most important types and functions:

// These are the four struct types that contain all GIF data and parameters:
typedef GIFConfig               // global cofinguration parameters of the GIF
typedef FrameConfig             // local configuration parameters for a frame
typedef GIF                    // struct for the full GIF
typedef Frame                  // struct for a single frame

// The user needs only these three functions to create a GIF image:
GIF* cgif_newgif    (GIFConfig* pConfig);               // creates a new GIF
int  cgif_addframe  (GIF* pGIF, FrameConfig* pConfig);  // adds a frame to an existing GIF
int  cgif_close     (GIF* pGIF);                      // close the created file and free memory

With our encoder you can create animated or static GIFs, you can or cannot use certain optimizations, and so on. You can switch between all these different options easily using the two attributes attrFlags and genFlags in the configurations GIFConfig and FrameConfig. These attributes are of type uint32_t and bundle yes/no-options with a bit-wise logic. So far only a few of the 32 bits are used leaving space to include further functionalities ensuring backward compatibility. We provide the following flag settings which can be combined by bit-wise or-operations:

GIF_ATTR_IS_ANIMATED          // make an animated GIF (default is non-animated GIF)
GIF_ATTR_NO_GLOBAL_TABLE      // disable global color table (global color table is default)
FRAME_ATTR_USE_LOCAL_TABLE    // use a local color table for a frame (not used by default)
FRAME_GEN_USE_TRANSPARENCY    // use transparency optimization (size optimization)
FRAME_GEN_USE_DIFF_WINDOW     // do encoding just for the sub-window that changed (size optimization)

If you didn't understand the point of attrFlags and genFlags and the flags, please don't worry. The example files cgif_example.c and cgif_example_video.c are all you need to get started and the used default settings for attrFlags and genFlags cover most cases quite well.

Compiling the example

An example can be compiled and tested simply by:

$ c99 -o cgif_example cgif_example_video.c cgif.c
$ ./cgif_example

Validating the encoder

In the folder tests, we provide several testing routines that you can be run via the script performtests.sh. To perform the tests you need to install the programs ImageMagick, gifsicle and tcc (tiny c compiler). With the provided tests you can validate that the encoder still generates correct GIF files after making changes on the encoder itself.

Further explanations

The GIF format employs the Lempel-Ziv-Welch (LZW) algorithm for image compression. If you are interested in details of the GIF format, please have a look at the official GIF documentation (https://www.w3.org/Graphics/GIF/spec-gif89a.txt).

License

Licensed under the MIT license (permissive). For more details please see LICENSE

Comments
  • Danke for this repo, a few thoughts/questions, possible funding opportunity

    Danke for this repo, a few thoughts/questions, possible funding opportunity

    Hi Daniel,

    I'm researching permissively-licenced open source GIF encoding libraries to add as a possible dependency of libvips and am very happy to discover this repo, including the huge bonus that it has tests :tada:

    I realise this code is less than a month old but it already seems to be well-written and fully-featured, which is a very good sign, thank you.

    I have a few questions after briefly reading and testing the code. I suspect these are things you've already thought of but not got round to yet so I can break these out into separate enhancement issues if that's easier.

    • Should constants/structs exposed by the header file be "namespaced" with a consistent CGIF prefix, e.g. CGIFFrameConfig, CGIF_FRAME_ATTR_... etc.?
    • Would allowing for output other than the filesystem e.g. to memory or pipe/stream be something of interest, perhaps by adding a configurable write function pointer to the GIFConfig (CGIFConfig?) struct?
    • I notice there are comments about the endianess of converting 16-bit integers to byte arrays. Are big endian systems something you'd like to support?
    • An initial valgrind report suggests there may be cases where uninitialised memory is being used, where e.g. the use of calloc would be safer than malloc, but this is still something I need to test properly (all_optim appears to segfault randomly).
    • To make things easier for me locally, I've created a small meson configuration file for this repo as it removes most of the pain of (cross-)compilation/testing, as libvips supports many platforms, should that be of interest to you.

    I'd be happy to use some of our Open Collective donations to help fund this work, and/or submit PRs where appropriate too.

    question 
    opened by lovell 6
  • Add support for colo(u)r table with first entry as transparency

    Add support for colo(u)r table with first entry as transparency

    Guten Tag, I've taken the approach as suggested in #4, ensuring the relevant disposal method is set, plus added a unit test.

    I've also tested this using a botched together libvips+cgif integration and the obligatory dancing banana.

    banana

    enhancement 
    opened by lovell 5
  • Enhancement: support adding (first) frame with transparency

    Enhancement: support adding (first) frame with transparency

    It's common for GIF images to include transparent pixels as part of a frame (often as a single frame "animated" image) and typically this would be the first entry/index in the palette.

    I've had a quick go at hacking this into cgif via a FRAME_ATTR_HAS_TRANSPARENCY flag and it seems to work.

    diff --git a/cgif.c b/cgif.c
    index 81567c0..4154dd1 100644
    --- a/cgif.c
    +++ b/cgif.c
    @@ -556,7 +556,7 @@ int cgif_addframe(GIF* pGIF, FrameConfig* pConfig) {
         pFrame->aGraphicExt[1] = 0xF9;
         pFrame->aGraphicExt[2] = 0x04;
         pFrame->aGraphicExt[3] = 0x04;
    -    if(pFrame->config.genFlags & FRAME_GEN_USE_TRANSPARENCY) {
    +    if((pFrame->config.genFlags & FRAME_GEN_USE_TRANSPARENCY) || (pConfig->attrFlags & FRAME_ATTR_HAS_TRANSPARENCY)) {
           pFrame->aGraphicExt[3] |= 0x01;
         }
         pFrame->aGraphicExt[6] = pFrame->transIndex;
    diff --git a/cgif.h b/cgif.h
    index 80e617c..8fb0603 100644
    --- a/cgif.h
    +++ b/cgif.h
    @@ -10,6 +10,7 @@
     #define GIF_ATTR_IS_ANIMATED          (1uL << 1)       // make an animated GIF (default is non-animated GIF)
     #define GIF_ATTR_NO_GLOBAL_TABLE      (1uL << 2)       // disable global color table (global color table is default)
     #define FRAME_ATTR_USE_LOCAL_TABLE    (1uL << 0)       // use a local color table for a frame (local color table is not used by default)
    +#define FRAME_ATTR_HAS_TRANSPARENCY   (1uL << 1)       // palette contains transparency at index 0
     // flags to decrease GIF-size
     #define FRAME_GEN_USE_TRANSPARENCY    (1uL << 0)       // use transparency optimization (setting pixels identical to previous frame transparent)
     #define FRAME_GEN_USE_DIFF_WINDOW     (1uL << 1)       // do encoding just for the sub-window that has changed from previous frame
    

    It's quite likely there's a better/cleaner way to do this.

    I'm happy to submit a PR but would welcome any thoughts/improvements on the approach/API.

    enhancement 
    opened by lovell 3
  • Add meson build configuration

    Add meson build configuration

    Hi, as discussed in #1 this PR adds a meson build script to allow all the major Linux distributions, macOS Homebrew etc. to start to build, package and distribute this library.

    The tests have been updated so they write to their "own" file to allow these to be run in parallel.

    It adds GitHub Actions CI configuration, including running the tests via valgrind.

    https://github.com/lovell/cgif/actions/runs/1067901844

    opened by lovell 3
  • Unable to make animated GIF that doesn't loop

    Unable to make animated GIF that doesn't loop

    I'm trying to encode an animated GIF file that only plays once and then stops. However, when I set CGIF_Config->numLoops to 1, it plays twice, and when I set it to 0, it results in an infinite loop, making it impossible to do so. Example GIF: freeze (1)

    I re-encoded the GIF with ImageMagick to have a proper loop count, here's what it's supposed to look like: ben

    After comparing the two GIFs above in a hex editor, I can see that the one that properly loops doesn't have a NETSCAPE2.0 application extension. Perhaps there could be a way to specify that it's not needed?

    opened by TheEssem 2
  • Small memory leak when calling cgif_close() without having called cgif_addframe()

    Small memory leak when calling cgif_close() without having called cgif_addframe()

    There appears to be a small memory leak when cgif_close is called without first adding a frame by calling cgif_addframe. It appears cgif_close will skip some cleanups when pGIF->CurResult is != CGIF_OK. In this case, it’s CGIF_PENDING and so pGIF doesn’t get freed within cgif_raw_close. I appreciate this isn’t the most typical use case for cgif, but it is possible for it to happen in my application if it has to close down before having had chance to add a frame. Thanks for a great library.

    opened by inkmeister 2
  • Improve LZW encoding speed

    Improve LZW encoding speed

    Most time of encoding is spent in the LZW routines. Increase LZW encoding speed by improving cache affinity. The memory usage has been decreased as well.

    ToDo:

    • [x] Do benchmarks to verify the speed improvement
    • [x] Extend tests and add tests that fully utilize the pTreeMap (backup LZW mapping tree)
    opened by dloebl 2
  • sparc64: fix potential unaligned access

    sparc64: fix potential unaligned access

    Resolve potential unaligned accesses which caused issues on sparc64. This issue has been reported by the Debian Build-System: https://buildd.debian.org/status/package.php?p=cgif

    • [x] Set up sparc64 VM and test this fix
    • [x] Backport PR to V0.0 branch and release V0.0.4
    opened by dloebl 1
  • Fix minimum global / local color table size

    Fix minimum global / local color table size

    As of now, in case the user specifies a color table with 1 or 2 entries, the size of the resulting color table in the GIF is chosen two entries too large (2^2 instead of 2^1). Resolve this by properly calculating the needed size. This behavior can be reproduced with the min_color_table_test.c test.

    opened by dloebl 1
  • Improve `calcInitCodeLen` function

    Improve `calcInitCodeLen` function

    It seems that this can be implemented as log2(n - 1) + 2, though that will give different results for n < 3 || n > 256, but perhaps that's fine.

    This PR was initiated as a draft to discuss this further.

    Test program
    #include <stdio.h>
    #include <stdint.h>
    
    static uint8_t calcInitCodeLen(uint16_t numEntries) {
    	if (numEntries > (1uL << 7)) {
    		return 9;
    	}
    	if (numEntries > (1uL << 6)) {
    		return 8;
    	}
    	if (numEntries > (1uL << 5)) {
    		return 7;
    	}
    	if (numEntries > (1uL << 4)) {
    		return 6;
    	}
    	if (numEntries > (1uL << 3)) {
    		return 5;
    	}
    	if (numEntries > (1uL << 2)) {
    		return 4;
    	}
    	return 3;
    }
    
    static uint8_t calcInitCodeLen2(uint16_t numEntries) {
    	return 31 - __builtin_clz(numEntries - 1) + 2; // log2(n - 1) + 2
    }
    
    int main() {
    	uint16_t i;
    
    	for (i = 1; i < (1 << 10); i <<= 1 /*i++*/) {
    		printf("i = %d\n", i);
    		printf("%d ", calcInitCodeLen(i));
    		printf("%d\n", calcInitCodeLen2(i));
    	}
    }
    
    enhancement 
    opened by kleisauke 1
  • Allow creating GIF animations with no repetitions

    Allow creating GIF animations with no repetitions

    As mentioned in #46, the numLoops value in the NETSCAPE2.0 Loop Extension seems to be interpreted as the number of additional repetitions by most decoders. Add CGIF_ATTR_NO_LOOP flag which allows creating an animated GIF without the NETSCAPE2.0 Extension: The animation will run only one time. Fixes #46.

    opened by dloebl 0
  • Improve memory usage when writing interlaced GIFs

    Improve memory usage when writing interlaced GIFs

    As of now, cgif requires more memory for writing interlaced GIFs.

    For more details: https://github.com/dloebl/cgif/blob/b05de324c4413558bea330fb350ad4fd13396529/src/cgif_raw.c#L526

    enhancement 
    opened by dloebl 0
  • Reduce file size: clear code optimization

    Reduce file size: clear code optimization

    The file size of a frame after LZW encoding could be reduced further by using so-called clear codes. A clear code resets the LZW dictionary. Afterwards, the encoding is continued with a fresh LZW dictionary and the initial minimum code size. As of now, cgif always issues a clear code once the dictionary is full (4096 entries). However, it can be beneficial regarding the file size to reset it earlier or later. This is an optimization problem. We would need to find an adequate heuristic to find an approximate optimum here.

    enhancement 
    opened by dloebl 0
  • Add fuzzer

    Add fuzzer

    Add fuzzer for cgif which allows covering the complete API. An initial seed corpus can be dynamically generated from our unit tests (tests/).

    The seed corpus archive can be generated as follows: meson setup build -Dfuzzer=true meson test -C build > build/cgif_fuzzer_seed_corpus.zip

    ToDo:

    • [ ] Allow generating the seed corpus without having to invoke all unit tests.

    Note: Generating a seed from a unit test is done by wrapping the cgif API (fuzz/cgif_create_fuzz_seed.c). However, this wrapper does not emulate the cgif API entirely: Some unit tests are expected to fail when wrapped (e.g. tests/eindex.c).

    opened by dloebl 0
  • [WIP] Add cgif_rgb API

    [WIP] Add cgif_rgb API

    Add public cgif_rgb API with the following features:

    • Allows passing RGB(A) image data to cgif.
    • cgif takes care of quantization/dithering and creates the color palette(s).
    • Avoids the size explosion (libvips/libvips#2576) by keeping transparent areas transparent during quantization/dithering.

    ToDo:

    • [ ] improve dithering quality and enable it by default (potentially switch to quantizr)
    • [ ] add lossy transparency selection ("low-pass filter") to improve the size further
    • [ ] improve quantization quality (potentially switch to quantizr)
    • [ ] merge some local color palette(s) to a global palette (if the quality is not affected)
    • [ ] cleanup/refactor cgif_rgb module
    • [ ] add documentation to Wiki/README
    • [ ] add examples
    • [ ] add coverage tests for cgif_rgb module
    enhancement 
    opened by dloebl 0
  • Add tests that check whether the input matches the output image data

    Add tests that check whether the input matches the output image data

    As of now our tests only check whether the resulting file is a valid GIF image. However, we should add tests that check whether the input image data matches the output image data. To achieve this, we would need to pass the GIF image created to an actual GIF decoder.

    Steps:

    1. Find a common, well-tested decoder (e.g. ImageMagick might be an option)
    2. Add a compare routine to all current tests (Note: multi-frame / transparent GIFs might be tricky)
    enhancement 
    opened by dloebl 2
Releases(V0.3.0)
Owner
Daniel Löbl
Daniel Löbl
Demo of a fast PNG encoder.

Fast PNG Encoder This is a proof-of-concept fast PNG encoder that uses AVX2 and a special Huffman table to encode images faster. Speed on a single cor

Luca Versari 53 Sep 18, 2022
You can use this to compile the code and output images into a word doc for assignment purposes

Code_n_Ouput_to_docx You can use this to compile the code and output images into a word doc for assignment purposes Basic requirements: Python 3.7 or

Aaditya Prabu K 0 Aug 21, 2022
An open source library for face detection in images. The face detection speed can reach 1000FPS.

libfacedetection This is an open source library for CNN-based face detection in images. The CNN model has been converted to static variables in C sour

Shiqi Yu 11.2k Oct 4, 2022
A slim, fast and header-only GIF loader written in C

gif_load This is an ANSI C compatible animated GIF loader in a single header file of less than 300 lines of code (less than 200 without empty lines an

Andrey Guskov 67 Sep 9, 2022
Very fast C++ .PNG writer for 24/32bpp images.

fpng Very fast C++ .PNG writer for 24/32bpp images. fpng.cpp was written to see just how fast you can write .PNG's without sacrificing too much compre

Rich Geldreich 614 Sep 26, 2022
PNG encoder and decoder in C and C++.

PNG encoder and decoder in C and C++, without dependencies

Lode Vandevenne 1.7k Oct 1, 2022
Guetzli is a JPEG encoder that aims for excellent compression density at high visual quality

Guetzli is a JPEG encoder that aims for excellent compression density at high visual quality. Guetzli-generated images are typically 20-30% smaller than images of equivalent quality generated by libjpeg. Guetzli generates only sequential (nonprogressive) JPEGs due to faster decompression speeds they offer.

Google 12.7k Oct 1, 2022
Video, Image and GIF upscale/enlarge(Super-Resolution) and Video frame interpolation. Achieved with Waifu2x, SRMD, RealSR, Anime4K, RIFE, CAIN, DAIN and ACNet.

Video, Image and GIF upscale/enlarge(Super-Resolution) and Video frame interpolation. Achieved with Waifu2x, SRMD, RealSR, Anime4K, RIFE, CAIN, DAIN and ACNet.

Aaron Feng 7.8k Oct 2, 2022
An image and texture viewer for tga, png, apng, exr, dds, gif, hdr, jpg, tif, ico, webp, and bmp files

An image and texture viewer for tga, png, apng, exr, dds, gif, hdr, jpg, tif, ico, webp, and bmp files. Uses Dear ImGui, OpenGL, and Tacent. Useful for game devs as it displays information like the presence of an alpha channel and querying specific pixels for their colour.

Tristan Grimmer 146 Sep 14, 2022
PoC black/white image sequence to dumpy gif image sequence converter

PoC black/white image sequence to dumpy gif image sequence converter

null 67 Sep 24, 2022
Import GIF/WebP animated image as a new AnimatedTexture asset type.

Animated Texture Plugin for Unreal Engine 5 This plugin allows you to import animated pictures into your Unreal Engine 5 project as a new AnimatedText

房燕良 30 Sep 17, 2022
A GIF art engine that will allow you to generate multi-layer GIFs from single GIFs as layers.

A GIF art engine that will allow you to generate multi-layer GIFs from single GIFs as layers. All the code in this repository has been written by me in c++, inspired by the generative art engine of HashLips that does not support GIFs as layers. The problem arose from my and my teamleader's need to generate animated images and then GIFs, in the same way as HashLips generated static images.

Andre 60 Sep 8, 2022
A crude untested example showing how to retrieve and display images from multiple cameras with OpenCV and wxWidgets.

About wxOpenCVCameras is a crude untested example of how to retrieve and display images from multiple cameras, using OpenCV to grab images from a came

PB 6 Jul 13, 2022
Reading, writing, and processing images in a wide variety of file formats, using a format-agnostic API, aimed at VFX applications.

README for OpenImageIO Introduction The primary target audience for OIIO is VFX studios and developers of tools such as renderers, compositors, viewer

OpenImageIO 1.5k Oct 1, 2022
HDRView is a simple research-oriented image viewer with an emphasis on examining and comparing high-dynamic range (HDR) images

HDRView is a simple research-oriented high-dynamic range image viewer with an emphasis on examining and comparing images, and including minimalistic tonemapping capabilities. HDRView currently supports reading EXR, PNG, TGA, BMP, HDR, JPG, GIF, PNM, PFM, and PSD images and writing EXR, HDR, PNG, TGA, PPM, PFM, and BMP images.

Wojciech Jarosz 120 Sep 14, 2022
A simple API wrapper that generates images & facts of any animal

animality.h A simple API wrapper that generates images & facts of any animal Required dependencies: libcurl for sending HTTPS requests. pthreads for t

animality 5 Jun 23, 2022
Convert images to ASCII art.

Image-to-ascii Convert images to ASCII art. This program using stb library to load images. Usage Usage: compile the program to get *.out file

Abdeljalil Bouchfar 13 Aug 20, 2022
Make It Pixel is a programming language to process images to look like pixel art.

Make images look like pixel art Make It Pixel is a programming language to process images to look like pixel art. Its interpreter is written in C++ an

null 16 Sep 7, 2022
Given a set of images, LeastAverageImage generates the least average -- the opposite of the average.

LeastAverageImage Given a set of images, LeastAverageImage generates the least average -- the opposite of the average. More information about the prog

Andrew Eckel 5 Apr 12, 2022