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

       最近由于项目需要,需要先把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



 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值