FLAnimatedImageView处理gif过程

FLAnimatedImageView处理gif过程

时间控制原理

GIF图片每一帧的delayTime可能都不一样;

在展示下一帧的时间控制机制,不能根据以第一帧为准;

或总动画时长除以帧数来简单做平均值为准,

都是不太好的方案。

FLAnimatedImageView的控制方式,读取每一帧的delayTime算出最大公约数,用CADisplayLink来控制时间的,比如说(如下图),

第二帧到第三帧的控制:第二帧的delayTime=2s,第三帧的delayTime=3s,如果第二帧没到时间,FLAnimatedImageView的image数据保持不变,时间一到就从FLAnimatedImage中获取image,赋值给ImageView。

CADisplayLink处理过程

首先startAnimating中的CADisplayLink初始化,为了防止retain cycle 用了NSProxy weak语义的property,也可以用

__weak typeof(self) wself = self;
    ...
    // block中处理
    __strong typeof(wself) sself = wself;
    if (!sself) {
        return;
    }
    // 紧跟处理code

反正都与weak有关,都是固定套路了。

CADisplayLink的frameInterval,frameInterval = 1时,refreshRate = 60Hz,frameInterval = 2时,refreshRate = 30Hz; 
此处设置的

const NSTimeInterval kDisplayRefreshRate = 60.0; // 60Hz

// 最小的frameInterval = 1
self.displayLink.frameInterval = MAX([self frameDelayGreatestCommonDivisor] * kDisplayRefreshRate, 1);

接下来就是最关键的处理方法- (void)displayDidRefresh:(CADisplayLink *)displayLink

1,如果self.needsDisplayWhenImageBecomesAvailable==YES,调用[self.layer setNeedsDisplay];,标记layer需要刷新,下一次runloop中displayLayer方法会被调用;然后设置self.needsDisplayWhenImageBecomesAvailable=NO不到下一帧的时间,不改变ImageView的内容; 
赋值代码相当简单:

- (void)displayLayer:(CALayer *)layer
{
    // 从 image 方法中取currentFrame作为像是内容
    layer.contents = (__bridge id)self.image.CGImage;
}

2,累加时间值accumulator跟当前帧的delayTime比对,注释如下:

/**
 * duration属性提供了每帧之间的时间,也就是屏幕每次刷新之间的的时间,一个略小的值;
 * frameInterval相当一个>=1的固定值,每次refresh,accumulator都累加一小段时间
 **/
self.accumulator += displayLink.duration * displayLink.frameInterval;

// While-loop first inspired by & good Karma to: https://github.com/ondalabs/OLImageView/blob/master/OLImageView.m

/**
 * 1,如果self.accumulator < 当前的delayTime,直接跳过,下一次refresh,accumulator再累加,相当于这一次image的内容不变;
 * 2,只有当累加值self.accumulator >= 当前的delayTime(说明已经是累加到这一帧的delayTime的最后,下一帧的开头了),进入while循环,循环内部每次将累加器减掉delayTime(accumulator又回到0状态),以便跳出循环;currentFrameIndex累加,下次refresh时,获取下一帧图像数据;
 * 3,如果到达最后一帧,循环次数loopCountdown--,又跳转到首帧图像;
 * 4,最主要的一步标记self.needsDisplayWhenImageBecomesAvailable = YES;下一次refresh时,执行[self.layer setNeedsDisplay]; 进行新一帧图像数据的刷新。
 **/
while (self.accumulator >= delayTime) {
    self.accumulator -= delayTime;
    self.currentFrameIndex++;
    if (self.currentFrameIndex >= self.animatedImage.frameCount) {
        // If we‘ve looped the number of times that this animated image describes, stop looping.
        self.loopCountdown--;
        if (self.loopCompletionBlock) {
            self.loopCompletionBlock(self.loopCountdown);
        }

        if (self.loopCountdown == 0) {
            [self stopAnimating];
            return;
        }
        self.currentFrameIndex = 0;
    }
    // Calling `-setNeedsDisplay` will just paint the current frame, not the new frame that we may have moved to.
    // Instead, set `needsDisplayWhenImageBecomesAvailable` to `YES` -- this will paint the new image once loaded.
    self.needsDisplayWhenImageBecomesAvailable = YES;
}

至此就时间控制过程基本处理完成。 
另外FLAnimatedImage类,专门根据gif图片大小来限制缓存多少帧,当遇到memoryWarning时,如何重试处理;多次遇到memoryWarning,固定缓存帧数等等功能,而且没有用到dispatch_semaphore等加锁/解锁操作;性能有保证。

参考链接

http://engineering.flipboard.com/2014/05/animated-gif

https://github.com/Flipboard/FLAnimatedImage

时间: 2024-08-04 13:44:25

FLAnimatedImageView处理gif过程的相关文章

Linux下WebSphereV8.5.5.0 安装详细过程

Linux下WebSphereV8.5.5.0 安装详细过程 自WAS8以后安装包不再区别OS,一份介质可以安装到多个平台.只针对Installation Manager 进行了操作系统的区分 ,Websphere产品介质必须通过专门的工具Install Managere安装.进入IBM的官网http://www.ibm.com/us/en/进行下载.在云盘http://yun.baidu.com/share/linkshareid=2515770728&uk=4252782771 中是Linu

艰辛五天:Ubuntu14.04+显卡驱动+cuda+Theano环境安装过程

题记:从一开始不知道显卡就是GPU(虽然是学计算机的,但是我真的不知道-脑残如我也是醉了),到搞好所有这些环境前后弄了5天时间,前面的买显卡.装显卡和装双系统见另一篇博客装显卡.双系统,这篇主要记录我怎么配置后面的环境,虽然中间重装Ubuntu三次,后面安装过程也没差别. 基础平台:64-bit,Ubuntu14.04 1.安装NVIDIA驱动(参考技术文章,基本是复制啊,蟹蟹作者~) (1) 在官网下载NVIDIA驱动,根据自己买的型号选择下载,放到 /home/lvxia/ 目录下面,我下载

Win7 Qt4.8.5+QtCreator2.8.0+mingw配置过程

1:安装包 百度盘下载链接: Mingw: http://pan.baidu.com/share/link?shareid=3960359240&uk=4147081190 Qt Creator 2.8.0: http://pan.baidu.com/share/link?shareid=3964645350&uk=4147081190 Qt 4.8.5: http://pan.baidu.com/share/link?shareid=3968136805&uk=414708119

tomcat启动过程报the JDBC Driver has been forcibly unregistered问题的修复过程

最近两天在整理关于flume的总结文档,没有启动过tomcat.昨天晚上部署启动,发现报了如题的错误,全文如下: 严重: The web application [/oa-deploy] registered the JBDC driver [com.microsoft.sqlserver.jdbc.SQLServerDriver] but failed to unregister it when the web application was stopped. To prevent a mem

Android APP 调试过程中遇到的问题。

调试过过程中APP安装完启动后有的时候会异常退出,报这个错误.有的时候可以直接启动.查找不到原因.网上说把commit方法替换成commitAllowingStateLoss() 也无效. Android APP 调试过程中遇到的问题. >> android 这个答案描述的挺清楚的:http://www.goodpm.net/postreply/android/1010000007192169/AndroidAPP调试过程中遇到的问题.html

VMWARE VCSA 6.5安装过程

VMWARE  VCSA 6.5与6.0的安装过程有点小出入,特记录下来,方便工作. 一.在本地笔记本上将VCSA ISO解压缩 二.进入vcsa-ui-installer\win32目录 运行installer 三.在弹出的vCenter Server Appliance 6.5 Lnstaller中点击install 四.安装过程 五.在安装完成后,登录vcenter(https://192.168.105.38:5480)进行配置,如果登录不了,请先进esxi中确认vcsa安装成功并开启

微信小程序豆瓣电影项目的改造过程经验分享

在学习微信小程序开发过程中,一部分的难点是前端逻辑的处理,也就是对前端JS的代码编辑:一部分的难点是前端界面的设计展示:本篇随笔基于一个豆瓣电影接口的小程序开源项目进行重新调整,把其中遇到的相关难点和改进的地方进行讨论介绍,希望给大家提供一个参考的思路,本篇随笔是基于前人小程序的项目基础上进行的改进,因此在开篇之前首先对原作者的辛劳致敬及感谢. 1.豆瓣电影接口的小程序项目情况 豆瓣电影接口提供了很多相关的接口给我们使用,豆瓣电影接口的API地址如下所示:https://developers.d

2.4、uboot配置和编译过程详解

2.4.1.uboot主Makefile分析1 2.4.1.1.uboot version分析 (1)uboot版本号分为3个级别: VERSION:主版本号 PATCHLEVEL:次版本号 SUBLEVEL:再次版本号 EXTRAVERSION:另外附加的版本信息 这四个用.隔开共同构成了最终的版本号. (2)Makefile中版本号最终生成了一个变量U_BOOT_VERSION,这个变量记录了Makefile中配置的版本号 (3)include/version_autogenerated.h

15.1-全栈Java笔记:Java事件模型是什么?事件控制的过程有哪几步??

应用前边两节上一章节的内容,大家可以完成一个简单的界面,但是没有任何的功能,界面完全是静态的,如果要实现具体功能的话,必须要学习事件模型. 事件模型简介及常见事件模型 对于采用了图形用户界面的程序来说,事件控制是非常重要的. 一个源(事件源)产生一个事件并把它(事件对象)送到一个或多个监听器那里,监听器只是简单地等待,直到它收到一个事件,一旦事件被接收,监听器将处理这些事件. 一个事件源必须注册监听器以便监听器可以接收关于一个特定事件的通知. 每种类型的事件都有其自己的注册方法,一般形式为: v