重读YYImage有感

2020-09-29  本文已影响0人  重案组之虎曹达华_

前言

这篇文章写在我毕业工作的第五个年头,从前一直觉得自己挺牛的,什么东西都一学就会。

实至今日,才发现自己的能力在各方面是有多弱,和牛逼的人差距是有多大。计算机的很多基础知识都极为薄弱。

青春期最最最后悔的事,就是没有好好读书,天天想着怎么逃课,泡妞。

其实15年刚刚做iOS的时候就接触过YYImage,当时就是觉得挺屌的,大家都在吹,我也用呗。也没有好好读过源码。

最近因为业务需要,对各种图片格式也有了新的认识,索性重新来读一遍YYImage,顺便总结一些感想

YYImage是YYKit系列中的其中一个组件,大神自己口述是在大概搞iOS的第五个年头写出来的。

我自认为我在第五个年头,才能静下心来把源码好好地从头到尾理顺,而人家已经在第五个年头发布了这个iOS中国最优秀之一的框架。

其实自己跟别人比起来,真是没什么天赋,只有多花时间,少一些玩的时间。

正文

其实纵观整个YYImage还是基于ImageIO和CoreGraphics的相关api进行一系列操作的。

疑点总结

  1. 对于动图来说,假设其中一帧进度时间小于0.011秒,强制将这一帧时间提升到0.1秒。至于原因其实源码当中是有备注的。

    大致原因在于:许多恼人的广告指定0秒持续时间,以到达最快速度的闪烁,我们遵循Safari和Firefox的行为,对于指定持续时间<=10毫秒的任何帧,使用100毫秒的持续时间

    这也是ImageIO源码中的原话

    // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
    // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
    // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
    // for more information.
    

    至于为什么是100毫秒,下面其实也有说到,

    没有一个现代浏览器支持0.02秒以下的帧延迟。因此,当创建动画GIF时,绝不应使用低于此阈值的帧延迟,因为这将完全无效。另一个有趣的发现是Safari是唯一一个在GIF动画播放方面性能下降的浏览器。

    对于那些对创建和观看流畅、快速的动画感兴趣的人来说,Chrome、Firefox和Opera无疑是不错的选择。这些浏览器都支持0.02秒的最小帧延迟,从而使动画GIF以每秒50帧的速度运行。然而,这种能力需要与交叉兼容性问题进行权衡。

    由于Safari和internetexplorer决定只支持0.06秒的帧延迟(低于此值的四舍五入为0.10秒),动画的观看速度有可能大大低于预期。所讨论的GIF将以每秒10帧的速度播放,而不是期望的每秒50帧。只有20%的真实速度,这成为一个认真考虑美学影响。(翻译而来差不多看看)

  2. 对于单张静态图片的加载,YYImage和UImage的加载方式略有不同,同样是imageNamed:(NSString *)name方法,UImage不会解码,而是在UIImageView addView的时候才进行解码,而YYImage会在调用该方法后当场解码,因此放入异步线程进行解码还可以减少主线程压力。

  3. 几种常见的图片保存格式

    第一种是 baseline,即逐行扫描。默认情况下,JPEG、PNG、GIF 都是这种保存方式。
    第二种是 interlaced,即隔行扫描。PNG 和 GIF 在保存时可以选择这种格式。
    第三种是 progressive,即渐进式。JPEG 在保存时可以选择这种方式。
    调用CGImageSourceUpdateData(data, false) 可以实现类似于网页中的渐进式显示的效果。

  4. preloadAllAnimatedImageFrames该属性可支持提前解开所有帧图片,使在动图播放过程中减少cpu的开销,但是可能会造成oom,所以视情况而定进行使用。

  5. 强如webp也有他的性能瓶颈,那就是解码效率,没有方案是万能的,bitmap,png,wep各自都有他们的存在场景,假如以后计算机的存储量和网络流量都无限大,随便用,那么我完全可以使用bitmap,我为什么要使用webp去消耗额外的cpu或者gpu的开销呢。

  6. 其实YYImage当中还有很强大的encode功能,支持多种格式和参数(quality质量,lossless无损等),不过我个人在实际业务中用到的比较少。

遗留问题

读源码的过程中还是碰到某几处地方还是没有特别的理解,希望后续有机会能得到答案

  1. 关于在解码类的入口函数递归锁的使用

    - (BOOL)updateData:(NSData *)data final:(BOOL)final {
        BOOL result = NO;
        pthread_mutex_lock(&_lock);
        result = [self _updateData:data final:final];
        pthread_mutex_unlock(&_lock);
        return result;
    }
    

    我认为对于递归锁的本质来说,其实就是给递归方法用的。使得同一线程在同一区块可以进行重入,但是对于该方法,我来来回回看了很多遍都没发现怎么会出现重入的情况,所以对于这里还是挺疑惑的,这里用递归锁的意义。

  2. 关于缓冲区的下一帧的移除操作

    在CADisplayLink每次触发的step方法中有这么一段

    bufferedImage = buffer[@(nextIndex)];
    if (bufferedImage) {
     if ((int)_incrBufferCount < _totalFrameCount) {
         [buffer removeObjectForKey:@(nextIndex)];
     }
    ....
    } else {
         _bufferMiss = YES;
    }
    

    经过多次的调试,没太明白[buffer removeObjectForKey:@(nextIndex)]这句话的意义,因为由于子线程的解码速度一定是大于当前播放的帧数的,所以其实每次每次来到这个地方,nextIndex基本是解完了的,但这里先是在buffer中移除下一帧图片,但后续又在Operationmain重新拿到(虽然有缓存)进行添加的,因为buffer最终还是会保存所有的帧图片。而且我移除这句话也没发现啥问题,所以没有很搞懂移除的操作是为了啥

  3. 待学部分:png的图片格式数据解析,包括图片的解码算法。

感谢

移动端图片格式调研

iOS 处理图片的一些小 Tip

上一篇下一篇

猜你喜欢

热点阅读