/*---------------------------------------------------------------------------- ANSI C program to set display time of each frame in an animated GIF. Chris Giese http://SisAndHappy.com/ChrisGiese/ I, the copyright holder of this work, hereby release it into the public domain. This applies worldwide. If this is not legally possible: I grant any entity the right to use this work for any purpose, without any conditions, unless such conditions are required by law. 15 June 2019: initial version. ----------------------------------------------------------------------------*/ #include #include #include #if 0 #include #else typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #endif /****************************************************************************/ static void write_le16(void *buf0, unsigned val) { uint8_t *buf = buf0; buf[0] = val; val >>= 8; buf[1] = val; } /****************************************************************************/ int main(int arg_c, char *arg_v[]) { uint8_t buf[13]; uint32_t ms; unsigned j; char *s; FILE *f; int i; if(arg_c != 3) { printf( "Sets display time of each frame in an animated .GIF file\n" "Usage: giftime file.gif milliseconds\n"); return 1; } /* Number of milliseconds to display each frame in the animated .GIF The time value is stored in the .GIF file as a 16-bit unsigned value in units of 10 milliseconds, so each frame can be displayed for no longer than 65535 * 10 = 655350 milliseconds = 655 seconds = ~11 minutes. */ ms = strtoul(arg_v[2], &s, 10); if(s == arg_v[2] || ms == 0 || ms > 655350L) { printf("Error: invalid display time %lu ms\n", ms); return 2; } /* open .GIF file, validate */ if((f = fopen(arg_v[1], "r+b")) == NULL) { printf("Error: can't open file '%s'\n", arg_v[1]); return 3; } if(fread(buf, 1, 13, f) != 13 || memcmp(buf, "GIF8", 4)) { printf("Error: file '%s' is not a .GIF file\n", arg_v[1]); fclose(f); return 4; } /* skip global palette, if any */ if(buf[10] & 0x80) { j = buf[10] & 7; j = 2 << j; fseek(f, j * 3, SEEK_CUR); } while((i = fgetc(f)) != EOF) { printf("%9lu %c\t", ftell(f) - 1, i); /* end of .GIF file */ if(i == ';') break; /* image descriptor */ else if(i == ',') { if(fread(buf, 1, 9, f) != 9) READ_ERR: { printf("Error reading .GIF file '%s'\n", arg_v[1]); fclose(f); return 5; } /* skip local palette, if any */ if(buf[8] & 0x80) { j = buf[8] & 7; j = 2 << j; fseek(f, j * 3, SEEK_CUR); } /* skip initial LZW code size */ if(fgetc(f) == EOF) goto READ_ERR; /* skip blocks of LZW-compressed data (1-255 bytes each) */ while(1) { if((i = fgetc(f)) == EOF) goto READ_ERR; if(i == 0) break; fseek(f, i, SEEK_CUR); } } /* extension blocks */ else if(i == '!') { /* type of the block */ if((i = fgetc(f)) == EOF) goto READ_ERR; /* skip blocks other than graphic control extension (type 249) */ if(i != 249) { while(1) { if((i = fgetc(f)) == EOF) goto READ_ERR; if(i == 0) break; fseek(f, i, SEEK_CUR); } } else { uint32_t save; if(fread(buf, 1, 6, f) != 6) goto READ_ERR; if(buf[0] != 4 || buf[5] != 0) goto FMT_ERR; /* display time in the GCE block is in units of 10 milliseconds */ write_le16(&buf[2], (unsigned)(ms / 10)); save = ftell(f); fseek(f, -6, SEEK_CUR); printf("Changing display time to %lu ms", ms); if(fwrite(buf, 1, 6, f) != 6) { printf("Error writing .GIF file " "'%s'\n", arg_v[1]); fclose(f); return 6; } fseek(f, save, SEEK_SET); } } else FMT_ERR: { printf("Error in .GIF file '%s'\n", arg_v[1]); fclose(f); return 7; } printf("\n"); } printf("Success\n"); fclose(f); return 0; }