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 问题截图 如 ...
随机推荐
- spoj 274
离散化 枚举行 扫描横坐标 #include <iostream> #include <cstdio> #include <cstring> #include ...
- NData BUG 记录
一.collection 如果设计如下页面 页面模型如下 using UnityEngine; using System.Collections; using System.Collections.G ...
- js注册验证【转】
function getFocus() //设置用户名文本框获取焦点 { document.getElementById("txtuname").focus(); } functi ...
- ifram一些常用的知识点
本文摘自:http://www.cnblogs.com/duankaige/archive/2012/09/20/2695012.html iframe的调用包括以下几个方面:(调用包含html ...
- MySQL.. ERROR! The server quit without updating PID file问题解决
不小心将服务器OS给重启了,再启动数据库的时候,出现了很奇怪的问题 [root@dev run]# service mysql restart ERROR! MySQL server PID file ...
- 深入理解JVM—字节码执行引擎
原文地址:http://yhjhappy234.blog.163.com/blog/static/3163283220122204355694/ 前面我们不止一次的提到,Java是一种跨平台的语言,为 ...
- 查看mininet交换机中的流表
官网文档http://mininet.org/walkthrough/#xterm-display Xterms are also useful for running interactive com ...
- [hackerrank]Even Odd Query
https://www.hackerrank.com/contests/w5/challenges 简单题,注意整数的0次方是1,奇数. #include <iostream> #incl ...
- 利用Nginx搭建http和rtmp协议的流媒体服务器
http://www.linuxidc.com/Linux/2013-02/79118.htm
- Xamarin.Android 入门之:Listview和adapter
一.引言 不管开发什么软件,列表的使用是必不可少的,而本章我们将学习如何使用Xamarin去实现它,以及如何使用自定义适配器.关于xamarin中listview的基础和适配器可以查看官网https: ...