iOS - GIF图的完美拆解、合成、显示

时间:2023-03-09 17:56:33
iOS - GIF图的完美拆解、合成、显示

转:http://blog.csdn.net/marujunyy/article/details/14455699

最近由于项目需要,需要先把gif图拆解开,然后在每一张图片上添加一些图片和文字,最后再合成gif文件;写了一个工具类可以每一帧画面并遵循每一帧所对应的显示时间进行播放,并且可以用多张图片指定每一帧播放时间来合成gif图。下面是使用方法和工具类:(需要添加framework
: ImageIO、QuartzCore、MobileCoreServices)

  1. NSDate *date = [NSDate date];
  2. //读取本地GIF图中每一帧图像的信息
  3. NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"demo" withExtension:@"gif"];
  4. NSDictionary *dic = [GifView getGifInfo:fileUrl];
  5. NSMutableArray *imageArray = [NSMutableArray array];
  6. //在gif图的每一帧上面添加一段文字
  7. for(int index=0;index<[dic[@"images"] count];index++)
  8. {
  9. //绘制view 已GIf图中的某一帧为背景并在view上添加文字
  10. UIView *tempView = [[UIView alloc] initWithFrame:CGRectFromString(dic[@"bounds"])];
  11. tempView.backgroundColor = [UIColor colorWithPatternImage:dic[@"images"][index]];
  12. UILabel *tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 10, 80, 20)];
  13. tempLabel.text = @"GIF测试";
  14. tempLabel.textColor = [UIColor redColor];
  15. tempLabel.backgroundColor = [UIColor clearColor];
  16. tempLabel.font = [UIFont boldSystemFontOfSize:20];
  17. [tempView addSubview:tempLabel];
  18. //将UIView转换为UIImage
  19. UIGraphicsBeginImageContextWithOptions(tempView.bounds.size, NO, tempView.layer.contentsScale);
  20. [tempView.layer renderInContext:UIGraphicsGetCurrentContext()];
  21. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  22. [imageArray addObject:image];
  23. UIGraphicsEndImageContext();
  24. }
  25. //生成GIF图 -- loopCount 为0表示无限播放
  26. NSString *path = [GifView exportGifImages:[imageArray copy] delays:dic[@"delays"] loopCount:0];
  27. //在页面上展示合成之后的GIF图
  28. GifView *gifView = [[GifView alloc] initWithCenter:self.view.center fileURL:[NSURL fileURLWithPath:path]];
  29. [self.view addSubview:gifView];
  30. NSLog(@"合成GIF图用时:%f秒",[[NSDate date] timeIntervalSinceDate:date]);

iOS - GIF图的完美拆解、合成、显示

//GifView.h

  1. #import <UIKit/UIKit.h>
  2. @interface GifView : UIView
  3. /*
  4. * @brief desingated initializer
  5. */
  6. - (id)initWithCenter:(CGPoint)center fileURL:(NSURL*)fileURL;
  7. - (void)initFileURL:(NSURL*)fileURL;
  8. /*
  9. * @brief start Gif Animation
  10. */
  11. - (void)startGif;
  12. - (void)startGifAnimation;
  13. /*
  14. * @brief stop Gif Animation
  15. */
  16. - (void)stopGif;
  17. /*
  18. * @brief get frames image(CGImageRef) in Gif
  19. */
  20. + (NSDictionary *)getGifInfo:(NSURL *)fileURL;
  21. + (NSString *)exportGifImages:(NSArray *)images delays:(NSArray *)delays loopCount:(NSUInteger)loopCount;
  22. @end

//GifView.m

  1. //
  2. //  GifView.m
  3. //
  4. //  Created by marujun on 13-11-7.
  5. //  Copyright (c) 2013年 极致. All rights reserved.
  6. //
  7. #import "GifView.h"
  8. #import <ImageIO/ImageIO.h>
  9. #import <QuartzCore/QuartzCore.h>
  10. #import <MobileCoreServices/MobileCoreServices.h>
  11. @interface GifView() {
  12. NSMutableArray *_frames;
  13. NSMutableArray *_frameDelayTimes;
  14. CGPoint frameCenter;
  15. CADisplayLink *displayLink;
  16. int frameIndex;
  17. double frameDelay;
  18. NSUInteger _loopCount;
  19. NSUInteger _currentLoop;
  20. CGFloat _totalTime;         // seconds
  21. CGFloat _width;
  22. CGFloat _height;
  23. }
  24. @end
  25. @implementation GifView
  26. - (id)initWithCenter:(CGPoint)center fileURL:(NSURL*)fileURL;
  27. {
  28. self = [super initWithFrame:CGRectZero];
  29. if (self) {
  30. _frames = [[NSMutableArray alloc] init];
  31. _frameDelayTimes = [[NSMutableArray alloc] init];
  32. _width = 0;
  33. _height = 0;
  34. frameCenter = center;
  35. [self initFileURL:fileURL];
  36. }
  37. return self;
  38. }
  39. - (void)initFileURL:(NSURL*)fileURL
  40. {
  41. if (fileURL) {
  42. getFrameInfo((__bridge CFURLRef)fileURL, _frames, _frameDelayTimes, &_totalTime, &_width, &_height, _loopCount);
  43. }
  44. self.frame = CGRectMake(0, 0, _width, _height);
  45. self.center = frameCenter;
  46. self.backgroundColor = [UIColor clearColor];
  47. if(_frames && _frames[0]){
  48. self.layer.contents = (__bridge id)([_frames[0] CGImage]);
  49. }
  50. }
  51. //使用displayLink播放
  52. - (void)startGif
  53. {
  54. frameIndex = 0;
  55. _currentLoop = 1;
  56. frameDelay =[_frameDelayTimes[0] doubleValue];
  57. [self stopGif];
  58. displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateDisplay:)];
  59. [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  60. }
  61. //每秒60帧刷新视图
  62. - (void)updateDisplay:(CADisplayLink *)link
  63. {
  64. if(frameDelay<=0){
  65. frameIndex ++;
  66. if(_loopCount!=0){
  67. if (_currentLoop>=_loopCount) {
  68. [self stopGif];
  69. }else{
  70. _currentLoop ++;
  71. }
  72. }
  73. if(frameIndex>=_frames.count){
  74. frameIndex = 0;
  75. }
  76. frameDelay = [_frameDelayTimes[frameIndex] doubleValue]+frameDelay;
  77. self.layer.contents = (__bridge id)([_frames[frameIndex] CGImage]);
  78. }
  79. frameDelay -= fmin(displayLink.duration, 1);   //To avoid spiral-o-death
  80. }
  81. - (void)willMoveToSuperview:(UIView *)newSuperview
  82. {
  83. if(newSuperview){
  84. [self startGif];
  85. }else{
  86. [self stopGif];  //视图将被移除
  87. }
  88. }
  89. //使用Animation方式播放Gif
  90. - (void)startGifAnimation
  91. {
  92. [self stopGif];
  93. CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
  94. NSMutableArray *times = [NSMutableArray arrayWithCapacity:3];
  95. CGFloat currentTime = 0;
  96. int count = _frameDelayTimes.count;
  97. for (int i = 0; i < count; ++i) {
  98. [times addObject:[NSNumber numberWithFloat:(currentTime / _totalTime)]];
  99. currentTime += [[_frameDelayTimes objectAtIndex:i] floatValue];
  100. }
  101. [animation setKeyTimes:times];
  102. NSMutableArray *images = [NSMutableArray arrayWithCapacity:3];
  103. for (int i = 0; i < count; ++i) {
  104. [images addObject:(__bridge id)[[_frames objectAtIndex:i] CGImage]];
  105. }
  106. [animation setValues:images];
  107. [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
  108. animation.duration = _totalTime;
  109. animation.delegate = self;
  110. if(_loopCount<=0){
  111. animation.repeatCount = INFINITY;
  112. }else{
  113. animation.repeatCount = _loopCount;
  114. }
  115. [self.layer addAnimation:animation forKey:@"gifAnimation"];
  116. }
  117. - (void)stopGif
  118. {
  119. [self.layer removeAllAnimations];
  120. [self removeDisplayLink];
  121. if(_frames && _frames[0]){
  122. self.layer.contents = (__bridge id)([_frames[0] CGImage]);
  123. }
  124. }
  125. - (void)removeDisplayLink
  126. {
  127. [displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  128. [displayLink invalidate];
  129. displayLink = nil;
  130. }
  131. // remove contents when animation end
  132. - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
  133. {
  134. if(_frames && _frames[0]){
  135. self.layer.contents = (__bridge id)([_frames[0] CGImage]);
  136. }
  137. }
  138. /*
  139. * @brief 获取gif图中每一帧的信息
  140. */
  141. + (NSDictionary *)getGifInfo:(NSURL *)fileURL
  142. {
  143. NSMutableArray *frames = [NSMutableArray arrayWithCapacity:3];
  144. NSMutableArray *delays = [NSMutableArray arrayWithCapacity:3];
  145. NSUInteger loopCount = 0;
  146. CGFloat totalTime;         // seconds
  147. CGFloat width;
  148. CGFloat height;
  149. getFrameInfo((__bridge CFURLRef)fileURL, frames, delays, &totalTime, &width, &height, loopCount);
  150. NSDictionary *gifDic = @{@"images":frames,          //图片数组
  151. @"delays":delays,          //每一帧对应的延迟时间数组
  152. @"duration":@(totalTime),  //GIF图播放一遍的总时间
  153. @"loopCount":@(loopCount), //GIF图播放次数  0-无限播放
  154. @"bounds": NSStringFromCGRect(CGRectMake(0, 0, width, height))}; //GIF图的宽高
  155. return gifDic;
  156. }
  157. /*
  158. * @brief 指定每一帧播放时长把多张图片合成gif图
  159. */
  160. + (NSString *)exportGifImages:(NSArray *)images delays:(NSArray *)delays loopCount:(NSUInteger)loopCount
  161. {
  162. NSString *fileName = [NSString stringWithFormat: @"%.0f.%@", [NSDate timeIntervalSinceReferenceDate] * 1000.0, @"gif"];
  163. NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
  164. CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:filePath],
  165. kUTTypeGIF, images.count, NULL);
  166. if(!loopCount){
  167. loopCount = 0;
  168. }
  169. NSDictionary *gifProperties = @{ (__bridge id)kCGImagePropertyGIFDictionary: @{
  170. (__bridge id)kCGImagePropertyGIFLoopCount: @(loopCount), // 0 means loop forever
  171. }
  172. };
  173. float delay = 0.1; //默认每一帧间隔0.1秒
  174. for (int i=0; i<images.count; i++) {
  175. UIImage *itemImage = images[i];
  176. if(delays && i<delays.count){
  177. delay = [delays[i] floatValue];
  178. }
  179. //每一帧对应的延迟时间
  180. NSDictionary *frameProperties = @{(__bridge id)kCGImagePropertyGIFDictionary: @{
  181. (__bridge id)kCGImagePropertyGIFDelayTime: @(delay), // a float (not double!) in seconds, rounded to centiseconds in the GIF data
  182. }
  183. };
  184. CGImageDestinationAddImage(destination,itemImage.CGImage, (__bridge CFDictionaryRef)frameProperties);
  185. }
  186. CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)gifProperties);
  187. if (!CGImageDestinationFinalize(destination)) {
  188. NSLog(@"failed to finalize image destination");
  189. }
  190. CFRelease(destination);
  191. return filePath;
  192. }
  193. /*
  194. * @brief resolving gif information
  195. */
  196. void getFrameInfo(CFURLRef url, NSMutableArray *frames, NSMutableArray *delayTimes, CGFloat *totalTime,CGFloat *gifWidth, CGFloat *gifHeight,NSUInteger loopCount)
  197. {
  198. CGImageSourceRef gifSource = CGImageSourceCreateWithURL(url, NULL);
  199. //获取gif的帧数
  200. size_t frameCount = CGImageSourceGetCount(gifSource);
  201. //获取GfiImage的基本数据
  202. NSDictionary *gifProperties = (__bridge NSDictionary *) CGImageSourceCopyProperties(gifSource, NULL);
  203. //由GfiImage的基本数据获取gif数据
  204. NSDictionary *gifDictionary =[gifProperties objectForKey:(NSString*)kCGImagePropertyGIFDictionary];
  205. //获取gif的播放次数 0-无限播放
  206. loopCount = [[gifDictionary objectForKey:(NSString*)kCGImagePropertyGIFLoopCount] integerValue];
  207. CFRelease((__bridge CFTypeRef)(gifProperties));
  208. for (size_t i = 0; i < frameCount; ++i) {
  209. //得到每一帧的CGImage
  210. CGImageRef frame = CGImageSourceCreateImageAtIndex(gifSource, i, NULL);
  211. [frames addObject:[UIImage imageWithCGImage:frame]];
  212. CGImageRelease(frame);
  213. //获取每一帧的图片信息
  214. NSDictionary *frameDict = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(gifSource, i, NULL);
  215. //获取Gif图片尺寸
  216. if (gifWidth != NULL && gifHeight != NULL) {
  217. *gifWidth = [[frameDict valueForKey:(NSString*)kCGImagePropertyPixelWidth] floatValue];
  218. *gifHeight = [[frameDict valueForKey:(NSString*)kCGImagePropertyPixelHeight] floatValue];
  219. }
  220. //由每一帧的图片信息获取gif信息
  221. NSDictionary *gifDict = [frameDict valueForKey:(NSString*)kCGImagePropertyGIFDictionary];
  222. //取出每一帧的delaytime
  223. [delayTimes addObject:[gifDict valueForKey:(NSString*)kCGImagePropertyGIFDelayTime]];
  224. if (totalTime) {
  225. *totalTime = *totalTime + [[gifDict valueForKey:(NSString*)kCGImagePropertyGIFDelayTime] floatValue];
  226. }
  227. CFRelease((__bridge CFTypeRef)(frameDict));
  228. }
  229. CFRelease(gifSource);
  230. }
  231. - (void)dealloc
  232. {
  233. NSLog(@"%s",__FUNCTION__);
  234. }
  235. @end

参考:http://*.com/questions/14915138/create-and-and-export-an-animated-gif-via-ios