Skip to content

Instantly share code, notes, and snippets.

@SheffieldKevin
Last active November 26, 2018 18:22
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save SheffieldKevin/7110704 to your computer and use it in GitHub Desktop.
Save SheffieldKevin/7110704 to your computer and use it in GitHub Desktop.
I had far more difficulty getting the generation of gif animations working correctly using ImageIO and Core-Graphics aka Quartz on MacOS/iOS than should have reasonably been expected.
// main.m
// creategif
//
// Created by Kevin Meaney on 21/10/2013.
// I've briefly blogged about it here:
// http://blog.yvs.eu.com/2013/10/creating-gif-animations-using-coreimagequartz
// The following code is all that is all the code needed for a creating a command line tool to generate
// a gif animation from image files.
// The main limitation with this code is that apart from the frame delay time of 0.1 second, every
// other frame delay time has to be a multiple of 0.5 seconds. There is no checking that the frame size
// of each image file is the same or resizing of frames to make them conform.
// I provide no warranty as to the operation of this code.
#import <Foundation/Foundation.h>
@import ImageIO;
void PrintUsage()
{
printf("creategif - usage:\n");
printf(" ./creategif [-parameter <value> ...], filename1, filename2, ...\n");
printf(" Parameters are preceded by a -<parameterName>. The order of the parameters is unimportant.\n");
printf(" All the input image filenames follow the parameters and their values.\n");
printf(" You probably need to escape or quote paths especially if the paths includes spaces etc..\n");
printf(" If the output file already exists then it will be overwritten if possible.\n");
printf(" Available parameters are:\n");
printf(" -outputfile <output file path which should finish with a gif extension>\n");
printf(" -delaytime <should be a floating point number above 0.1>\n");
// printf(" -unclampeddelaytime <should be a floating point number above 0.1>\n");
printf(" Sample export lines:\n");
printf(" ./creategif -outputfile blahblah.gif -delaytime 0.4 -unclampeddelaytime 0.4 image1.png image2.png image3.png image4.png\n");
printf(" ./creategif -outputfile \"Users/ktam/gifs/blahblah.gif\" -delaytime 1.0 image1.png image2.png image3.png image4.png\n");
}
BOOL GetFloatFromString(NSString *string, CGFloat *value)
{
NSScanner *scanner = [[NSScanner alloc] initWithString:string];
CGFloat floatVal;
BOOL gotValue = [scanner scanDouble:&floatVal];
if (gotValue)
{
*value = floatVal;
}
return gotValue;
}
NSMutableArray *inputImageList;
NSString *outFilePath;
CGFloat delayTime;
CGFloat unclampedDelayTime;
BOOL gotDelayTime = NO;
BOOL gotUnclampedDelayTime = NO;
bool GetArgs(int argc, const char **argv) // returns true if successful.
{
bool success = true;
argv++;
argc--;
const char *args;
// Get the parameters
while (argc > 0 && **argv == '-')
{
args = *argv;
argv++;
argc--;
if ( ! strcmp ( args, "-outputfile" ) )
{
outFilePath = @(*argv++);
argc--;
}
else if ( ! strcmp( args, "-delaytime" ))
{
gotDelayTime = GetFloatFromString(@(*argv++), &delayTime);
argc--;
}
/* else if ( ! strcmp( args, "-unclampeddelaytime"))
{
gotUnclampedDelayTime = GetFloatFromString(@(*argv++), &unclampedDelayTime);
argc--;
}
*/
}
if (!(gotDelayTime || gotUnclampedDelayTime))
success = false;
if (!outFilePath)
success = false;
// If we've got to the end of arguments and there are no input files then bail.
if (!success)
return success;
inputImageList = [[NSMutableArray alloc] initWithCapacity:0];
// [inputImageList addObject:@(args)];
// Finished getting parameters. Now get list of source files. 1 image per file.
while (argc > 0)
{
args = *argv;
argv++;
argc--;
[inputImageList addObject:@(args)];
}
if (!inputImageList || [inputImageList count] == 0)
success = false;
return success;
}
int main(int argc, const char * argv[])
{
@autoreleasepool
{
if (!GetArgs(argc, argv))
{
PrintUsage();
return 0;
}
NSDictionary *gifFileProps = @{ @"{GIF}" : @{ @"HasGlobalColorMap" : @YES,
@"LoopCount" : @(0) } };
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outFilePath isDirectory:NO];
CGImageDestinationRef imageDest;
imageDest = CGImageDestinationCreateWithURL((__bridge CFURLRef)outputURL,
CFSTR("com.compuserve.gif"),
[inputImageList count],
(__bridge CFDictionaryRef)gifFileProps);
if (!imageDest)
{
printf("Couldn't create image destination\n");
return 0;
}
CGImageDestinationSetProperties(imageDest,
(__bridge CFDictionaryRef)gifFileProps);
for (NSString *inputFile in inputImageList)
{
NSString *expandedString = [inputFile stringByExpandingTildeInPath];
NSURL *sourceFile = [[NSURL alloc] initFileURLWithPath:expandedString
isDirectory:NO];
CGImageSourceRef imageSource;
imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)sourceFile,
NULL);
if (!imageSource)
{
printf("Invalid source file path\n");
return 0;
}
if (CGImageSourceGetCount(imageSource))
{
CGImageRef theImage;
theImage = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
if (theImage)
{
NSMutableDictionary *gifFrameProps;
gifFrameProps = [[NSMutableDictionary alloc] initWithCapacity:0];
if (gotDelayTime)
[gifFrameProps setObject:@(delayTime) forKey:@"DelayTime"];
// if (gotUnclampedDelayTime)
// [gifFrameProps setObject:@(unclampedDelayTime)
// forKey:@"UnclampedDelayTime"];
NSDictionary *frameProps = @{ @"{GIF}" : gifFrameProps };
CGImageDestinationAddImage(imageDest, theImage,
(__bridge CFDictionaryRef)frameProps);
CFRelease(theImage);
}
}
CFRelease(imageSource);
}
CGImageDestinationFinalize(imageDest);
CFRelease(imageDest);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment