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 问题截图 如 ...
随机推荐
- java调用dll文件中的类型转换
char *转String (env)->NewStringUTF("the content you want to type in"); char *转jbyteArr ...
- Amazon 面经
[版面:待字闺中][首篇作者:gmadj] , 2013年09月29日21:51:33 [首页] [上页][下页][末页] [分页:1 2 ] gmadj 进入未名形象秀 我的博客 [回复] ...
- Sublime Text 3配置与vim模式(待完整)
Sublime Text 3通过设置默认值与用户值的方式,来进行配置.默认值不允许更改,用户值是用户进行配置.同一属性,当用户值存在时,默认值就无效.打开Preference,如图: 先贴下我的Set ...
- Relevance Between Variable Declaration and Definition in C++
A declaration makes a name known to a programm. A definition creates the assocatied entity. A variab ...
- IP地址总结
1.网际协议IP : 网际协议 IP 是 TCP/IP 体系中两个最主要的协议之一.与 IP 协议配套使用的还有四个协议: 地址解析协议 ARP (Address Resolution Protoco ...
- java基础知识回顾之java Thread类学习(六)--java多线程同步函数用的锁
1.验证同步函数使用的锁----普通方法使用的锁 思路:创建两个线程,同时操作同一个资源,还是用卖票的例子来验证.创建好两个线程t1,t2,t1线程走同步代码块操作tickets,t2,线程走同步函数 ...
- HBase入门
/×××××××××××××××××××××××××××××××××××××××××/ Author:xxx0624 HomePage:http://www.cnblogs.com/xxx0624/ ...
- Java 网络编程 字符流的发送与接收 自定义数据边界
在网络编程中,客户端调用了flush方法,就会将缓存在字符流中的文本发送给服务器,服务器该怎样判断客户端发送的文本已经结束了呢? 我们先看一个例子: 客户端: import java.io.IOExc ...
- Lambda Action Func练习
namespace lambda { delegate void TestDelegate(string s); class Program { static void Main(string[] a ...
- Win7 64 安装Visual Studio 2010和SQL Server 2008 R2
1. 在MSDN,我告诉你下载安装文件,VS 2010 不论32位还是64位都是同一个文件,cn_visual_studio_2010_ultimate_x86_dvd_532347.iso.SQL下 ...