有没有办法提高 iOS 下 gif 的性能啊?

2014-12-04 22:08:59 +08:00
 jox
UIImageView渲染gif的原理应该是使用CAKeyframeAnimation来配置layer的contents属性,我用layer也能实现gif的渲染,并且跟使用UIImageView的性能差不多,而且UIImageView的animationImages和CAKeyframeAnimation的values属性都是copy的,这样就不能使用放在缓存里的已经解码过的图片来渲染gif的frame,导致有的时候滚动到含有gif的cell会抽一下。

额,有些新手朋友大概不知道,UIImage内部是使用Image I/O来解码图片的,但是这个解码如果不强制执行的话,只有在要渲染的时候才会进行解码,如果保留UIImage指针的话,之后使用这个UIImage来渲染图片就不需要解码了,只需要渲染,所以静态图片只要解码过一次可以很快地渲染,但是gif动画就不行了,因为在给UIImageView或者layer提供gif的时候不是retain是copy过去的,这样就用不了之前已经解码过的图片数据了,额,我感觉这个应该是有意这么做的,可能是怕开发者乱渲染gif导致内存太高进而导致应用被杀死。

因为是copy的,提前强制解码也不好使了,我看百度贴吧的iOS应用是直接放弃gif了,网页版的gif到了手机上就全是静态的了。。。。我估摸着他们也遇到了同样的问题。

有没有iPhone上的应用渲染gif这块儿做的不错的?有人知道吗?非常感谢
10537 次点击
所在节点    iDev
19 条回复
matrix67
2014-12-04 22:55:51 +08:00
因为好像是因为费电
jox
2014-12-04 23:00:54 +08:00
@matrix67 额,对,gif会导致cpu和gpu不停地工作,确实会费电。。。。我在考虑要不要也学别人只渲染第一帧意思意思得了。。。
vixvix
2014-12-04 23:04:41 +08:00
狗了下, Flipboard有篇文章和源码,也许对你有用:
http://engineering.flipboard.com/2014/05/animated-gif/
gluttony
2014-12-04 23:22:36 +08:00
我以前试过各种gif解码库,都没有把图片下载到本地后套个静态HTML模版用UIWebView展示高效和省资源(很多段子应用都用的这个方法)。不过那时Flipboard的库还没出,你可以和UIWebView对比下。
jox
2014-12-04 23:23:57 +08:00
@vixvix 嘿,我也刚刚找到了这篇文章,我下载下来了源码看了一下,他们是使用的CADisplayLink,然后手动渲染layer的内容,这样就可以不必重复解码了,我还不太清楚CADisplayLink怎么用,这个应该是我需要的,不过同样非常感谢
jox
2014-12-04 23:27:48 +08:00
@gluttony UIWebView不行,不可能每个tablecell里都加载个UIWebView,最开始我就用过web view,效果不满意,而且有一些问题绕不过去,其实只要修改每帧的尺寸即使一下载入好几十帧也不怎么占内存,但是想要丝般顺滑,必须要跳过重复解码的过程。flipboard的方法应该能够解决我的问题,我需要好好研究一下他们的源代码
Smartype
2014-12-05 17:07:32 +08:00
@jox 其实没有你想得那么复杂的。就是把所有的帧都解出来,然后主动绘制。什么叫做主动绘制呢?就是像游戏一样,使用cadisplaylink让系统通知你绘制。

话说滚动的时候os会停止uiimageview的绘制来保证滚动的流畅性吧?这是你说的抽么?
jox
2014-12-05 17:28:42 +08:00
@Smartype flipboard的做法就是使用CADisplayLink的,他们的做法是在需要刷新的时候直接使用解压缩过的图片来设置layer的contents属性。所以可以提前强制解压,或者只在第一次渲染的时候解压缩,后续的渲染保证流畅。

如果使用UIImageVIew来渲染的话,使用UIImage的-(UIImage *)animatedImageWithImages:(NSArray *)images duration:(NSTimeInterval)duration
来创建gif,然后直接set UIImageVIew的image属性也可以渲染gif,我不清楚UIImageView是怎么实现的,但是这么做的话的确会导致卡顿,因为UIImageView的animationImages的属性是copy,所以我猜测内部实现是使用的CAKeyframeAnimation,CAKeyframeAnimation来实现gif的话就是要把每一帧都copy过去,Image I/O要想直接使用解压缩过的数据似乎得保留UIImage指针,这个应该很容易就能测试出来,copy的话就不是之前的指针了,于是就又解压缩了一次,这个是在主线程发生的,否则无法解释卡顿现象啊。
Smartype
2014-12-07 14:10:46 +08:00
@jox 我的意思是拖动的时候应该是挂起了runloop中的timer。导致卡顿的不是复制或者解码这些操作。
和uikit不同,cadisplaylink是用来做游戏的,是要在拖动的时候渲染的。这是我的理解。

如果是你认为是资源不足的原因的话,可以用一个小得可怜的gif试试。看拖动的时候是不是不会渲染。
jox
2014-12-07 16:07:30 +08:00
@Smartype 今天我拿Instruments测试了一下,证明我之前的猜测是错误的,你说的没错,导致卡顿的原因不是因为解码,我用一个超大的gif,然后使用同一个UIImage来创建UIImageView,然后加到当前view的hierarchy里,只有第一次是需要解码的,后面就都不用解码了,这是Time Profiler的截图:

lldong
2014-12-08 15:51:59 +08:00
像 Twitter 一样转成视频播放
Smartype
2014-12-08 20:25:49 +08:00
@jox 所以现在问题很明了了。是因为uiImageview的动画是用cftimer驱动的,而这个timer所在的runloop在滚动的时候被被挂起了,解决的方法要么是处理滚动,续上timer,不让出现跳帧。要么就是cadisplaylink,滚还是不滚,我都要播放。但是滚动的时候我是不关心动画的,我的话,会让滚动完了接着放
jox
2014-12-08 20:56:30 +08:00
@Smartype 我也打算在滚动结束之后再继续播放动画!非常感谢!

不过只要gif已经显示了,uiimageview在滚动的时候动画会一直播放,并且滚动很流畅,现在卡顿只有在接下来的需要渲染的table row里有很多数据的时候才会发生
Smartype
2014-12-08 21:29:30 +08:00
@jox 那你可以异步渲染 tableviewcell 嘛
jox
2014-12-08 22:01:11 +08:00
@Smartype 渲染只能在主线程做吧?怎么异步渲染啊?
Smartype
2014-12-08 23:51:12 +08:00
用operation queue或者dispatch asyn在绘制在一个image graphics context里,然后好了后直接把这个丢给main thread显示就好,应该可以做到丝般润滑
jox
2014-12-09 00:04:59 +08:00
@Smartype 这个我知道的,我以为你的意思是GPU渲染都能异步进行呢。我现在就是这么做的,拿到数据后在后台把文字都画到image context上,画好后把image设为layer的contents作为背景,然后再把图片异步加进去。就是现在图片这块儿还没处理好,如果一个cell里有好几个gif的话会卡顿。
jox
2014-12-27 14:27:54 +08:00
这个帖子是错误的,使用Image I/O创建的UIImage对象copy之后还是会使用解码过的图片数据,很尴尬,这里我搞错了一些东西,假设有这样的两个属性:

@property (retain, nonatomic) UIImage *retainImage;
@property (copy, nonatomic) UIImage *copyImage;

使用Image I/O创建了一个开启缓存的图片,并分别赋值给上面两个属性

UIImage *image = ...

self.retainImage = image;
self.copyImage = image;

到这里一共存在三个类型为UIImage的指针,两个类型为UIImage的对象,一个是使用Image I/O创建的,一个是在赋值给copyImage属性的时候创建的,ARC会分别计算这两个UIImage对象的引用数来决定是否要释放这些对象所占用的内存,但是需要指出的是,这两个UIImage对象里的_imageRef指向的是同一个地址!Image I/O是根据这个地址来决定是否要使用解码过的图片的,那么我的这个帖子根本就是错误的,如果有人被我误导了,在这里我向你道歉。

也许苹果开发swift的原因之一就是因为有些开发者会像我一样对跟指针相关的东西产生困惑吧,虽然苹果在让使用obj c的开发者尽量远离手动管理内存方面做了很多努力,但是只要使用指针就难免要和程序背后冰冷的机器打交道,哈,不过也不知道v2ex上有多少人能看出来我的这个帖子是错误的呢?如果有人看出来却没有说出来,我只能说兄弟你不厚道啊。
ichanne
2015-08-06 10:49:55 +08:00
最近在做多个gif动态表情一起发的功能, 卡顿啊

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/151599

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX