在 Cocoa 中创建动画 GIF - 定义帧类型
Posted
技术标签:
【中文标题】在 Cocoa 中创建动画 GIF - 定义帧类型【英文标题】:Creating an animated GIF in Cocoa - defining frame type 【发布时间】:2013-03-13 04:05:56 【问题描述】:我已经能够修改 SO 上的一些代码,以从我的视图的“屏幕截图”中生成动画 GIF,但结果是不可预测的。 GIF 帧有时是完整的图像、完整的帧(“替换”模式,正如 GIMP 标记的那样),其他时候只是与前一层的“差异”(“组合”模式)。
据我所见,当涉及的帧更少和/或更小时,CG 会以“组合”模式写入 GIF,但无法正确设置颜色。实际上,运动部件的颜色正确,背景是错误的。 当 CG 将 GIF 保存为全帧时,颜色还可以。文件大小更大,但是,嘿,显然你不能两全其美。 :)
有没有办法:
a) force CG to create "full frames" when saving the GIF
b) fix the colors (color table?)
我所做的是(ARC 模式):
使用
捕获视图的可见部分[[scrollView contentView] dataWithPDFInsideRect:[[scrollView contentView] visibleRect]];
将其转换为PNG类型的NSImageBitmapRep并调整其大小
-(NSMutableDictionary*) pngImageProps:(int)quality
NSMutableDictionary *pngImageProps;
pngImageProps = [[NSMutableDictionary alloc] init];
[pngImageProps setValue:[NSNumber numberWithBool:NO] forKey:NSImageInterlaced];
double compressionF = 1;
[pngImageProps setValue:[NSNumber numberWithFloat:compressionF] forKey:NSImageCompressionFactor];
return pngImageProps;
-(NSData*) resizeImageToData:(NSData*)data toDimX:(int)xdim andDimY:(int)ydim withQuality:(int)quality
NSImage *image = [[NSImage alloc] initWithData:data];
NSRect inRect = NSZeroRect;
inRect.size = [image size];
NSRect outRect = NSMakeRect(0, 0, xdim, ydim);
NSImage *outImage = [[NSImage alloc] initWithSize:outRect.size];
[outImage lockFocus];
[image drawInRect:outRect fromRect:inRect operation:NSCompositeCopy fraction:1];
NSBitmapImageRep* bitmapRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:outRect];
[outImage unlockFocus];
NSMutableDictionary *imageProps = [self pngImageProps:quality];
NSData* imageData = [bitmapRep representationUsingType:NSPNGFileType properties:imageProps];
return [imageData copy];
获取 BitmapReps 数组并创建 GIF
-(CGImageRef) pngRepDataToCgImageRef:(NSData*)data
CFDataRef imgData = (__bridge CFDataRef)data;
CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData (imgData);
CGImageRef image = CGImageCreateWithPNGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);
return image;
////////// create GIF from
NSArray *images; // holds all BitmapReps
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:pot],
kUTTypeGIF,
allImages,
NULL);
// set frame delay
NSDictionary *frameProperties = [NSDictionary
dictionaryWithObject:[NSDictionary
dictionaryWithObject:[NSNumber numberWithFloat:0.2f]
forKey:(NSString *) kCGImagePropertyGIFDelayTime]
forKey:(NSString *) kCGImagePropertyGIFDictionary];
// set gif color properties
NSMutableDictionary *gifPropsDict = [[NSMutableDictionary alloc] init];
[gifPropsDict setObject:(NSString *)kCGImagePropertyColorModelRGB forKey:(NSString *)kCGImagePropertyColorModel];
[gifPropsDict setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCGImagePropertyGIFHasGlobalColorMap];
// set gif loop
NSDictionary *gifProperties = [NSDictionary
dictionaryWithObject:gifPropsDict
forKey:(NSString *) kCGImagePropertyGIFDictionary];
// loop through frames and add them to GIF
for (int i=0; i < [images count]; i++)
NSData *imageData = [images objectAtIndex:i];
CGImageRef imageRef = [self pngRepDataToCgImageRef:imageData];
CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef) (frameProperties));
// save the GIF
CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)(gifProperties));
CGImageDestinationFinalize(destination);
CFRelease(destination);
我检查了 ImageBitmapReps,当单独保存为 PNG 时,它们很好。 据我了解,颜色表应该由CG处理还是我负责产生抖动的颜色?该怎么做?
即使重复制作相同的动画,生成的 GIF 也可能会有所不同。
这是一个 BitmapRep
(来源:andraz.eu)
这是带有无效颜色的 GIF(“组合”模式) (来源:andraz.eu)
【问题讨论】:
【参考方案1】:我阅读了您的代码。请在创建 CGImageDestinationRef 时仔细检查“allImages”和“[images count]”。
以下测试代码运行良好:
NSDictionary *prep = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.2f] forKey:(NSString *) kCGImagePropertyGIFDelayTime] forKey:(NSString *) kCGImagePropertyGIFDictionary];
CGImageDestinationRef dst = CGImageDestinationCreateWithURL((__bridge CFURLRef)(fileURL), kUTTypeGIF, [filesArray count], nil);
for (int i=0;i<[filesArray count];i++)
//load anImage from array
...
CGImageRef imageRef=[anImage CGImageForProposedRect:nil context:nil hints:nil];
CGImageDestinationAddImage(dst, imageRef,(__bridge CFDictionaryRef)(prep));
bool fileSave = CGImageDestinationFinalize(dst);
CFRelease(dst);
【讨论】:
是的,有一些缺失的代码,我现在看到了。 allImages = [图像数];和 -(CGImageRef) pngRepDataToCgImageRef:(NSData*)data CFDataRef imgData = (__bridge CFDataRef)data; CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData (imgData); CGImageRef image = CGImageCreateWithPNGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);返回图像;我有一个 NSImageBitmapRep-s 数组,只要有足够的帧或它们足够大,一切正常。我会再调查一下。 嗨,你有没有得到这个工作,我有一个类似的问题,不是所有的颜色都出现在输出的 gif 中...... 是的,成功了。问题是颜色表。获取输入图像,将颜色数减少到 256(最大 GIF)并为每个帧单独构建自定义调色板(颜色图)。以上是关于在 Cocoa 中创建动画 GIF - 定义帧类型的主要内容,如果未能解决你的问题,请参考以下文章
在 R 中创建 GIF 时出现帧顺序问题(库:“magick”和“purrr”)