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 问题截图 如 ...
随机推荐
- uva 1423 拓扑排序
刘书上例题 拓扑排序 #include <cstdio> #include <cstdlib> #include <cmath> #include <map ...
- hdu 1242 Rescue(BFS,优先队列,基础)
题目 /******************以下思路来自百度菜鸟的程序人生*********************/ bfs即可,可能有多个’r’,而’a’只有一个,从’a’开始搜,找到的第一个’r ...
- POJ 1844
#include <iostream> #define MAXN 20 using namespace std; int value[MAXN]; int place[MAXN]; ]; ...
- POJ2513Colored Sticks
http://poj.org/problem?id=2513 题意 : 一些木棒,两端都涂上颜色,求是否能将木棒首尾相接,连成一条直线,要求不同木棒相接的一边必须是相同颜色的. 思路 : 这个题的话就 ...
- 使用 C# 对文件进行压缩和解压
C#中对文件压缩和可以使用两个类: GZipStream 类 此实例分为几个模块,分别为: 压缩函数: /// <summary> /// 压缩文件 /// </summary> ...
- 缓存初解(五)---SpringMVC基于注解的缓存配置--web应用实例
之前为大家介绍了如何使用spring注解来进行缓存配置 (EHCache 和 OSCache)的简单的例子,详见 Spring基于注解的缓存配置--EHCache AND OSCache 现在介绍一下 ...
- JAVA! static什么作用?
是静态修饰符,什么叫静态修饰符呢?大家都知道,在程序中任何变量或者代码都是在编译时由系统自动分配内存来存储的,而所谓静态就是指在编译后所分配的内存会一直存在,直到程序退出内存才会释放这个空间,也就是只 ...
- Ios 弹框 MJPopup,KxMenu
IOS 弹框 如果直接弹出一个自定义的视图 可以选用第三方: MJPopup 弹出: if(!bandview) { bandview=[[[NSBundle mainBundle]loadNibNa ...
- [转]c#调用API截图
转自http://blog.csdn.net/hailiannanhai/article/details/6281471 要想完成这个功能,首先要了解一下在C#中如何调用API(应用程序接口)函数.虽 ...
- JDBC学习总结(三)
1.ResultSet光标控制 在创建Statement或PreparedStatement时使用的是Connection的无参数createStatement()方法或preparedSta ...