Skip to content

Commit

Permalink
Merge pull request #48 from dloebl/no-loop
Browse files Browse the repository at this point in the history
Allow creating GIF animations with no repetitions
  • Loading branch information
dloebl authored Mar 2, 2022
2 parents d41f277 + 2ed2466 commit 339cd48
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ With our encoder you can create animated or static GIFs, you can or cannot use c
CGIF_ATTR_IS_ANIMATED // make an animated GIF (default is non-animated GIF)
CGIF_ATTR_NO_GLOBAL_TABLE // disable global color table (global color table is default)
CGIF_ATTR_HAS_TRANSPARENCY // first entry in color table contains transparency (alpha channel)
CGIF_ATTR_NO_LOOP // run GIF animation only one time. numLoops is ignored (no repetitions)
CGIF_FRAME_ATTR_USE_LOCAL_TABLE // use a local color table for a frame (not used by default)
CGIF_FRAME_ATTR_HAS_ALPHA // frame contains alpha channel (index set via transIndex field)
CGIF_FRAME_ATTR_HAS_SET_TRANS // transparency setting provided by user (transIndex field)
Expand Down
1 change: 1 addition & 0 deletions inc/cgif.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern "C" {
#define CGIF_ATTR_IS_ANIMATED (1uL << 1) // make an animated GIF (default is non-animated GIF)
#define CGIF_ATTR_NO_GLOBAL_TABLE (1uL << 2) // disable global color table (global color table is default)
#define CGIF_ATTR_HAS_TRANSPARENCY (1uL << 3) // first entry in color table contains transparency (alpha channel)
#define CGIF_ATTR_NO_LOOP (1uL << 4) // don't loop a GIF animation: only play it one time.
#define CGIF_FRAME_ATTR_USE_LOCAL_TABLE (1uL << 0) // use a local color table for a frame (local color table is not used by default)
#define CGIF_FRAME_ATTR_HAS_ALPHA (1uL << 1) // alpha channel index provided by user (transIndex field)
#define CGIF_FRAME_ATTR_HAS_SET_TRANS (1uL << 2) // transparency setting provided by user (transIndex field)
Expand Down
3 changes: 2 additions & 1 deletion inc/cgif_raw.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ extern "C" {

// flags to set the GIF attributes
#define CGIF_RAW_ATTR_IS_ANIMATED (1uL << 0) // make an animated GIF (default is non-animated GIF)
#define CGIF_RAW_ATTR_NO_LOOP (1uL << 1) // don't loop a GIF animation: only play it one time.

// flags to set the Frame attributes
#define CGIF_RAW_FRAME_ATTR_HAS_TRANS (1uL << 0) // provided transIndex should be set
Expand All @@ -29,7 +30,7 @@ typedef struct {
uint16_t width; // effective width of each frame in the GIF
uint16_t height; // effective height of each frame in the GIF
uint16_t sizeGCT; // size of the global color table (GCT)
uint16_t numLoops; // number of repetitons of an animated GIF (set to INFINITE_LOOP for infinite loop)
uint16_t numLoops; // number of repetitons of an animated GIF (set to INFINITE_LOOP resp. 0 for infinite loop, use CGIF_ATTR_NO_LOOP if you don't want any repetition)
} CGIFRaw_Config;

// CGIFRaw_FrameConfig type
Expand Down
4 changes: 2 additions & 2 deletions meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
project(
'libcgif',
'c',
version : '0.2.1',
version : '0.3.0',
license : 'MIT',
default_options : ['c_std=c99'],
)
Expand All @@ -12,7 +12,7 @@ lib = library(
cgif_sources,
include_directories : ['inc/'],
soversion : '0',
version : '0.2.1',
version : '0.3.0',
install : true,
)

Expand Down
1 change: 1 addition & 0 deletions src/cgif.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ CGIF* cgif_newgif(CGIF_Config* pConfig) {
rawConfig.sizeGCT = (pConfig->attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE) ? 0 : pConfig->numGlobalPaletteEntries;
// translate CGIF_ATTR_* to CGIF_RAW_ATTR_* flags
rawConfig.attrFlags = (pConfig->attrFlags & CGIF_ATTR_IS_ANIMATED) ? CGIF_RAW_ATTR_IS_ANIMATED : 0;
rawConfig.attrFlags |= (pConfig->attrFlags & CGIF_ATTR_NO_LOOP) ? CGIF_RAW_ATTR_NO_LOOP : 0;
rawConfig.width = pConfig->width;
rawConfig.height = pConfig->height;
rawConfig.numLoops = pConfig->numLoops;
Expand Down
3 changes: 2 additions & 1 deletion src/cgif_raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,8 @@ CGIFRaw* cgif_raw_newgif(const CGIFRaw_Config* pConfig) {
rWrite |= writeDummyBytes(pConfig->pWriteFn, pConfig->pContext, numBytesLeft);
}
// GIF should be animated? => init & write app extension header ("NETSCAPE2.0")
if(pConfig->attrFlags & CGIF_RAW_ATTR_IS_ANIMATED) {
// No loop? Don't write NETSCAPE extension.
if((pConfig->attrFlags & CGIF_RAW_ATTR_IS_ANIMATED) && !(pConfig->attrFlags & CGIF_RAW_ATTR_NO_LOOP)) {
initAppExtBlock(aAppExt, pConfig->numLoops);
rWrite |= pConfig->pWriteFn(pConfig->pContext, aAppExt, SIZE_APP_EXT);
}
Expand Down
1 change: 1 addition & 0 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ tests = [
'more_than_256_colors',
'noise256',
'noise6',
'noloop',
'one_full_block',
'only_local_table',
'overlap_everything',
Expand Down
71 changes: 71 additions & 0 deletions tests/noloop.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <string.h>
#include <stdio.h>

#include "cgif.h"

#define WIDTH 50
#define HEIGHT 50

static void initGIFConfig(CGIF_Config* pConfig, uint8_t* pGlobalPalette, uint16_t numColors, uint32_t attrFlags, uint16_t width, uint16_t height) {
memset(pConfig, 0, sizeof(CGIF_Config));
pConfig->attrFlags = attrFlags;
pConfig->width = width;
pConfig->height = height;
pConfig->pGlobalPalette = pGlobalPalette;
pConfig->numGlobalPaletteEntries = numColors;
pConfig->path = "noloop.gif";
}

static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay, uint32_t genFlags) {
memset(pConfig, 0, sizeof(CGIF_FrameConfig));
pConfig->pImageData = pImageData;
pConfig->delay = delay;
pConfig->genFlags = genFlags;
}

int main(void) {
CGIF* pGIF;
CGIF_Config gConfig;
CGIF_FrameConfig fConfig;
uint8_t* pImageData;
uint8_t aPalette[] = {
0xFF, 0xFF, 0xFF, // white
0xFF, 0x00, 0x00, // red
0x00, 0xFF, 0x00, // green
};
cgif_result r;
const uint16_t numColors = 3; // number of colors in aPalette
//
// create new GIF
initGIFConfig(&gConfig, aPalette, numColors, CGIF_ATTR_IS_ANIMATED | CGIF_ATTR_NO_LOOP, WIDTH, HEIGHT);
pGIF = cgif_newgif(&gConfig);
if(pGIF == NULL) {
fputs("failed to create new GIF via cgif_newgif()\n", stderr);
return 1;
}
//
// add frames to GIF
pImageData = malloc(WIDTH * HEIGHT); // Actual image data
memset(pImageData, 0, WIDTH * HEIGHT);
initFrameConfig(&fConfig, pImageData, 50, CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW);
r = cgif_addframe(pGIF, &fConfig); // append the new frame
memset(pImageData, 1, WIDTH * HEIGHT);
r = cgif_addframe(pGIF, &fConfig); // append the next frame
memset(pImageData, 2, WIDTH * HEIGHT);
r = cgif_addframe(pGIF, &fConfig); // append the next frame
//
free(pImageData);
//
// Free allocated space at the end of the session
r = cgif_close(pGIF);

// check for errors
if(r != CGIF_OK) {
fprintf(stderr, "failed to create GIF. error code: %d\n", r);
return 2;
}
return 0;
}
1 change: 1 addition & 0 deletions tests/tests.md5
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ f2bfeb47a7ecc834ae21140f02c60297 min_size.gif
bcf6674e99f31e67da5bc17ad93cb7a7 more_than_256_colors.gif
c6a8e6f6d6d0c969cb300ea7eb18391d noise256.gif
7de24bdbff1dec81aab1840bcf69b19f noise6.gif
92336ea86024feb0a65bc21fde9bee61 noloop.gif
3337de21095a6b19effb40c518af96e5 one_full_block.gif
a7c04cb7b6d19a16023497da03384408 only_local_table.gif
013513307101f1d6b80164cdb3318c1c overlap_everything.gif
Expand Down

0 comments on commit 339cd48

Please sign in to comment.