iOS - GIF图的完美拆解、合成、显示
转:http://blog.csdn.net/marujunyy/article/details/14455699
最近由于项目需要,需要先把gif图拆解开,然后在每一张图片上添加一些图片和文字,最后再合成gif文件;写了一个工具类可以每一帧画面并遵循每一帧所对应的显示时间进行播放,并且可以用多张图片指定每一帧播放时间来合成gif图。下面是使用方法和工具类:(需要添加framework
: ImageIO、QuartzCore、MobileCoreServices)
- NSDate *date = [NSDate date];
- //读取本地GIF图中每一帧图像的信息
- NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"demo" withExtension:@"gif"];
- NSDictionary *dic = [GifView getGifInfo:fileUrl];
- NSMutableArray *imageArray = [NSMutableArray array];
- //在gif图的每一帧上面添加一段文字
- for(int index=0;index<[dic[@"images"] count];index++)
- {
- //绘制view 已GIf图中的某一帧为背景并在view上添加文字
- UIView *tempView = [[UIView alloc] initWithFrame:CGRectFromString(dic[@"bounds"])];
- tempView.backgroundColor = [UIColor colorWithPatternImage:dic[@"images"][index]];
- UILabel *tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 10, 80, 20)];
- tempLabel.text = @"GIF测试";
- tempLabel.textColor = [UIColor redColor];
- tempLabel.backgroundColor = [UIColor clearColor];
- tempLabel.font = [UIFont boldSystemFontOfSize:20];
- [tempView addSubview:tempLabel];
- //将UIView转换为UIImage
- UIGraphicsBeginImageContextWithOptions(tempView.bounds.size, NO, tempView.layer.contentsScale);
- [tempView.layer renderInContext:UIGraphicsGetCurrentContext()];
- UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
- [imageArray addObject:image];
- UIGraphicsEndImageContext();
- }
- //生成GIF图 -- loopCount 为0表示无限播放
- NSString *path = [GifView exportGifImages:[imageArray copy] delays:dic[@"delays"] loopCount:0];
- //在页面上展示合成之后的GIF图
- GifView *gifView = [[GifView alloc] initWithCenter:self.view.center fileURL:[NSURL fileURLWithPath:path]];
- [self.view addSubview:gifView];
- NSLog(@"合成GIF图用时:%f秒",[[NSDate date] timeIntervalSinceDate:date]);
//GifView.h
- #import <UIKit/UIKit.h>
- @interface GifView : UIView
- /*
- * @brief desingated initializer
- */
- - (id)initWithCenter:(CGPoint)center fileURL:(NSURL*)fileURL;
- - (void)initFileURL:(NSURL*)fileURL;
- /*
- * @brief start Gif Animation
- */
- - (void)startGif;
- - (void)startGifAnimation;
- /*
- * @brief stop Gif Animation
- */
- - (void)stopGif;
- /*
- * @brief get frames image(CGImageRef) in Gif
- */
- + (NSDictionary *)getGifInfo:(NSURL *)fileURL;
- + (NSString *)exportGifImages:(NSArray *)images delays:(NSArray *)delays loopCount:(NSUInteger)loopCount;
- @end
//GifView.m
- //
- // GifView.m
- //
- // Created by marujun on 13-11-7.
- // Copyright (c) 2013年 极致. All rights reserved.
- //
- #import "GifView.h"
- #import <ImageIO/ImageIO.h>
- #import <QuartzCore/QuartzCore.h>
- #import <MobileCoreServices/MobileCoreServices.h>
- @interface GifView() {
- NSMutableArray *_frames;
- NSMutableArray *_frameDelayTimes;
- CGPoint frameCenter;
- CADisplayLink *displayLink;
- int frameIndex;
- double frameDelay;
- NSUInteger _loopCount;
- NSUInteger _currentLoop;
- CGFloat _totalTime; // seconds
- CGFloat _width;
- CGFloat _height;
- }
- @end
- @implementation GifView
- - (id)initWithCenter:(CGPoint)center fileURL:(NSURL*)fileURL;
- {
- self = [super initWithFrame:CGRectZero];
- if (self) {
- _frames = [[NSMutableArray alloc] init];
- _frameDelayTimes = [[NSMutableArray alloc] init];
- _width = 0;
- _height = 0;
- frameCenter = center;
- [self initFileURL:fileURL];
- }
- return self;
- }
- - (void)initFileURL:(NSURL*)fileURL
- {
- if (fileURL) {
- getFrameInfo((__bridge CFURLRef)fileURL, _frames, _frameDelayTimes, &_totalTime, &_width, &_height, _loopCount);
- }
- self.frame = CGRectMake(0, 0, _width, _height);
- self.center = frameCenter;
- self.backgroundColor = [UIColor clearColor];
- if(_frames && _frames[0]){
- self.layer.contents = (__bridge id)([_frames[0] CGImage]);
- }
- }
- //使用displayLink播放
- - (void)startGif
- {
- frameIndex = 0;
- _currentLoop = 1;
- frameDelay =[_frameDelayTimes[0] doubleValue];
- [self stopGif];
- displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateDisplay:)];
- [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- }
- //每秒60帧刷新视图
- - (void)updateDisplay:(CADisplayLink *)link
- {
- if(frameDelay<=0){
- frameIndex ++;
- if(_loopCount!=0){
- if (_currentLoop>=_loopCount) {
- [self stopGif];
- }else{
- _currentLoop ++;
- }
- }
- if(frameIndex>=_frames.count){
- frameIndex = 0;
- }
- frameDelay = [_frameDelayTimes[frameIndex] doubleValue]+frameDelay;
- self.layer.contents = (__bridge id)([_frames[frameIndex] CGImage]);
- }
- frameDelay -= fmin(displayLink.duration, 1); //To avoid spiral-o-death
- }
- - (void)willMoveToSuperview:(UIView *)newSuperview
- {
- if(newSuperview){
- [self startGif];
- }else{
- [self stopGif]; //视图将被移除
- }
- }
- //使用Animation方式播放Gif
- - (void)startGifAnimation
- {
- [self stopGif];
- CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
- NSMutableArray *times = [NSMutableArray arrayWithCapacity:3];
- CGFloat currentTime = 0;
- int count = _frameDelayTimes.count;
- for (int i = 0; i < count; ++i) {
- [times addObject:[NSNumber numberWithFloat:(currentTime / _totalTime)]];
- currentTime += [[_frameDelayTimes objectAtIndex:i] floatValue];
- }
- [animation setKeyTimes:times];
- NSMutableArray *images = [NSMutableArray arrayWithCapacity:3];
- for (int i = 0; i < count; ++i) {
- [images addObject:(__bridge id)[[_frames objectAtIndex:i] CGImage]];
- }
- [animation setValues:images];
- [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
- animation.duration = _totalTime;
- animation.delegate = self;
- if(_loopCount<=0){
- animation.repeatCount = INFINITY;
- }else{
- animation.repeatCount = _loopCount;
- }
- [self.layer addAnimation:animation forKey:@"gifAnimation"];
- }
- - (void)stopGif
- {
- [self.layer removeAllAnimations];
- [self removeDisplayLink];
- if(_frames && _frames[0]){
- self.layer.contents = (__bridge id)([_frames[0] CGImage]);
- }
- }
- - (void)removeDisplayLink
- {
- [displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- [displayLink invalidate];
- displayLink = nil;
- }
- // remove contents when animation end
- - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
- {
- if(_frames && _frames[0]){
- self.layer.contents = (__bridge id)([_frames[0] CGImage]);
- }
- }
- /*
- * @brief 获取gif图中每一帧的信息
- */
- + (NSDictionary *)getGifInfo:(NSURL *)fileURL
- {
- NSMutableArray *frames = [NSMutableArray arrayWithCapacity:3];
- NSMutableArray *delays = [NSMutableArray arrayWithCapacity:3];
- NSUInteger loopCount = 0;
- CGFloat totalTime; // seconds
- CGFloat width;
- CGFloat height;
- getFrameInfo((__bridge CFURLRef)fileURL, frames, delays, &totalTime, &width, &height, loopCount);
- NSDictionary *gifDic = @{@"images":frames, //图片数组
- @"delays":delays, //每一帧对应的延迟时间数组
- @"duration":@(totalTime), //GIF图播放一遍的总时间
- @"loopCount":@(loopCount), //GIF图播放次数 0-无限播放
- @"bounds": NSStringFromCGRect(CGRectMake(0, 0, width, height))}; //GIF图的宽高
- return gifDic;
- }
- /*
- * @brief 指定每一帧播放时长把多张图片合成gif图
- */
- + (NSString *)exportGifImages:(NSArray *)images delays:(NSArray *)delays loopCount:(NSUInteger)loopCount
- {
- NSString *fileName = [NSString stringWithFormat: @"%.0f.%@", [NSDate timeIntervalSinceReferenceDate] * 1000.0, @"gif"];
- NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
- CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:filePath],
- kUTTypeGIF, images.count, NULL);
- if(!loopCount){
- loopCount = 0;
- }
- NSDictionary *gifProperties = @{ (__bridge id)kCGImagePropertyGIFDictionary: @{
- (__bridge id)kCGImagePropertyGIFLoopCount: @(loopCount), // 0 means loop forever
- }
- };
- float delay = 0.1; //默认每一帧间隔0.1秒
- for (int i=0; i<images.count; i++) {
- UIImage *itemImage = images[i];
- if(delays && i<delays.count){
- delay = [delays[i] floatValue];
- }
- //每一帧对应的延迟时间
- NSDictionary *frameProperties = @{(__bridge id)kCGImagePropertyGIFDictionary: @{
- (__bridge id)kCGImagePropertyGIFDelayTime: @(delay), // a float (not double!) in seconds, rounded to centiseconds in the GIF data
- }
- };
- CGImageDestinationAddImage(destination,itemImage.CGImage, (__bridge CFDictionaryRef)frameProperties);
- }
- CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)gifProperties);
- if (!CGImageDestinationFinalize(destination)) {
- NSLog(@"failed to finalize image destination");
- }
- CFRelease(destination);
- return filePath;
- }
- /*
- * @brief resolving gif information
- */
- void getFrameInfo(CFURLRef url, NSMutableArray *frames, NSMutableArray *delayTimes, CGFloat *totalTime,CGFloat *gifWidth, CGFloat *gifHeight,NSUInteger loopCount)
- {
- CGImageSourceRef gifSource = CGImageSourceCreateWithURL(url, NULL);
- //获取gif的帧数
- size_t frameCount = CGImageSourceGetCount(gifSource);
- //获取GfiImage的基本数据
- NSDictionary *gifProperties = (__bridge NSDictionary *) CGImageSourceCopyProperties(gifSource, NULL);
- //由GfiImage的基本数据获取gif数据
- NSDictionary *gifDictionary =[gifProperties objectForKey:(NSString*)kCGImagePropertyGIFDictionary];
- //获取gif的播放次数 0-无限播放
- loopCount = [[gifDictionary objectForKey:(NSString*)kCGImagePropertyGIFLoopCount] integerValue];
- CFRelease((__bridge CFTypeRef)(gifProperties));
- for (size_t i = 0; i < frameCount; ++i) {
- //得到每一帧的CGImage
- CGImageRef frame = CGImageSourceCreateImageAtIndex(gifSource, i, NULL);
- [frames addObject:[UIImage imageWithCGImage:frame]];
- CGImageRelease(frame);
- //获取每一帧的图片信息
- NSDictionary *frameDict = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(gifSource, i, NULL);
- //获取Gif图片尺寸
- if (gifWidth != NULL && gifHeight != NULL) {
- *gifWidth = [[frameDict valueForKey:(NSString*)kCGImagePropertyPixelWidth] floatValue];
- *gifHeight = [[frameDict valueForKey:(NSString*)kCGImagePropertyPixelHeight] floatValue];
- }
- //由每一帧的图片信息获取gif信息
- NSDictionary *gifDict = [frameDict valueForKey:(NSString*)kCGImagePropertyGIFDictionary];
- //取出每一帧的delaytime
- [delayTimes addObject:[gifDict valueForKey:(NSString*)kCGImagePropertyGIFDelayTime]];
- if (totalTime) {
- *totalTime = *totalTime + [[gifDict valueForKey:(NSString*)kCGImagePropertyGIFDelayTime] floatValue];
- }
- CFRelease((__bridge CFTypeRef)(frameDict));
- }
- CFRelease(gifSource);
- }
- - (void)dealloc
- {
- NSLog(@"%s",__FUNCTION__);
- }
- @end
参考:http://stackoverflow.com/questions/14915138/create-and-and-export-an-animated-gif-via-ios
iOS - GIF图的完美拆解、合成、显示的更多相关文章
- iOS启动图和开屏广告图,类似网易
iOS启动图和开屏广告图,类似网易 启动图是在iOS开发过程中必不可少的一个部分,很多app在启动图之后会有一张自定义的开屏广告图,点击该广告图可以跳转到广告图对应的页面.今天呢,和大家分享一下如何添 ...
- bzoj 1006: [HNOI2008]神奇的国度 弦图的染色问题&&弦图的完美消除序列
1006: [HNOI2008]神奇的国度 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 1788 Solved: 775[Submit][Stat ...
- iOS多图上传
iOS多图上传涉及到多线程问题,个人比较喜欢使用GCD操作,下边是最近写的一个多图上传代码,附带相关注释 __block BOOL allSucc = YES; __block int m = 0; ...
- iOS启动图launchImage设置后在启动时无法显示
iOS设置启动图: 会发现运行APP不显示设置好的启动图 解决方法: 卸载之前运行的APP,检查以下配置,将LaunchScreen删除即可. 原因: launchImage 是在没有LaunchSc ...
- iOS gif图显示问题
问题 有时候需要显示gif动态图,让界面更加的绚丽,但是iOS默认只支持png,gpg图片.那么如何才能显示gif图呢? 解决方式 添加框架 CoreGraphics.framework ImageI ...
- Ios 该图显示其出现的相关问题定义UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
解决这个问题 在 加上个 标示符 Cell 自己定义 customCell .h 代码例如以下 ViewController.m 文件里 代码例如以下 执行结果 吕 图坚持直接在这里 不行
- IOS GCD图片数据异步下载,下载完成后合成显示
关于GCD使用详解,请看我的上一篇blog:http://www.cnblogs.com/xin-lang/p/6278606.html 前段时间遇到个需要异步下载,下载完成后再组合显示的东西.这里我 ...
- iOS 切图使用 分辨率 使用 相关总结
疑问: 就iphone来说分辨率有三种,320*480. 640*960. 640*1136 那么如果我想做图,如果是320*480 1.是不是所有的图片,比如按钮的,背景图的,尺寸都必须做成小于或等 ...
- IOS关于XIB文件和调试时候显示不一样问题
1 前言 今天工作中,遇到了一个xib文件布局问题,具体问题如下:在xib中加了一个图片,背景为已经切好的图片,但是当显示在模拟器上面的时候却显示不出来效果. 2 详述 2.1 问题截图 如 ...
随机推荐
- Google 网站打不开
http://209.116.186.246/ http://91.213.30.153/ (2014年6月30日 新增) https://wen.lu/ (2014年6月30日 新增,注意下是ht ...
- 盘点 DevOps 世界的杰出女性(一)
[编者按]IT 领域从来不缺乏杰出的女性存在,近日,DevOps.com 主编 Alan Shimel 盘点了 DevOps 领域的杰出女性,首期为六个,本文系 OneAPM 工程师编译整理. 以下为 ...
- Embedding Lua in C: Using Lua from inside C.
Requirments: 1: The Lua Sources. 2: A C compiler - cc/gcc/g++ for Unix, and Visual C++ for Wi ...
- 在C#中调用另一个应用程序或命令行(.exe 带参数)<zz>
在.net中使用system.diaglostics.Process可以用来调用另一个命令行或程序. using System.Diagnostics; 如果是dos Proces ...
- ZOJ 3791 An Easy Game(DP)
题目链接 题意 : 给你两个长度为N的字符串,将第一个字符串每次只能变化M个,问变换K次之后变成第二个字符串一共有几种方法. 思路 : DP.dp[i][j]表示变了 i 次之后有j个不一样的字母的方 ...
- zoj 3599 Game 博弈论
K倍动态减法游戏!!! 链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4683 代码如下: #include<ios ...
- 解决maven-dependency-plugin (goals "copy-dependencies", "unpack") is not supported by m2e.错误
POM文件报错maven-dependency-plugin (goals "copy-dependencies", "unpack") is not supp ...
- APAC Practice
2016 round A A. Googol String small && large LL a[]; int dfs(LL pos, int id, bool f) { || po ...
- codeforces div.1 A
A. Efim and Strange Grade time limit per test 1 second memory limit per test 256 megabytes input sta ...
- s3cmd的安装与使用
s3cmd 是一款 Amazon S3 命令行工具.它不仅能上传.下载.同步,还能设置权限,下面是完整的安装使用指南. 主要是还是用来储存日志文件或者其他什么资料. https://wangyan. ...