OpenGL 图片从文件渲染到屏幕的过程

如题所述

第1个回答  2022-05-31

实际上iPhone有两个帧缓冲区,一个叫屏幕缓冲区,一个叫离屏缓冲区。

从上述流程可以看出,图片的显示是通过CPU和GPU的配合完成,这其中会出现一个问题,就是在GPU收到V-Sync信号时,CPU和GPU开始工作,在下一个V-Sync信号出现时CPU和GPU都没有处理完自己的工作,导致此时的离屏缓冲区中没有帧数据,就会出现 卡顿现象 。 更详细的解释可以参考ibireme大神的 iOS 保持界面流畅的技巧 。

在开始探索图片加载的流程前,先弄清楚两个概念。

我们先说说CPU都要做什么?

当使用UIImage的几个方法加载图片时,图片并不会立刻解码,它会经过一系列的步骤

以上就是着色器的渲染过程。

看到这里,我们已经知道 图片解码 过程非常耗时,对于App来说,静止的倒还好说,滑动的列表下,性能影响就会很严重了。 我们通常会用帧数FPS(Frames Per Second)来衡量手机的性能问题,60FPS(每秒60帧)是衡量会不会卡顿的标准,换算过来1帧的处理时间不应该超过0.1666s即16.7ms,接下来学习一下UIImage和YYImage的处理方式有什么可取之处。

UIImage有多种方法可以获取图片,那这些不同方法的区别是什么呢? 一、imageNamed

imageNamed 方式,在第一次渲染到屏幕上时会在主线程进行图片解码操作,位图数据会被缓存起来,之后再访问这个图片时,就从缓存获取了,看网上有说在手机发出内存警告时会清除UIImage的缓存。有两个问题: 1、第一次的图片解码操作还是在主线程做的 2、位图数据如果非常大,这么存到缓存里不太好。

二、imageWithContentsOfFile和imageWithData

imageWithContentsOfFile 和 imageWithData 在第一次渲染到屏幕时同样会在主线程进行图片解码操作,若把 UIImage 对象释放掉以后,再访问还是会出现在主线程进行图片解码的操作,这个小icon还好,大图就是有问题

所以一般市面上的做法是把 图片解码 强制提前执行,即在CPU进行解压缩前使用 CGBitmapContextCreate 在子线程中对其重新绘制。

找了YYKit的Demo直接拿来用了,YYImage的调用方式如下

YYImage集成自UIImage,重写了 imageNamed 方法避免了系统的全局缓存

这里面基本上就是从磁盘获取图片的二进制数据的过程。 scales 是类似@[@1,@2,@3]的数组,由于不同机型的物理分辨率或逻辑分辨率不同,相应的查询优先级也不同。 YYImage的多个方法都统一调用 initWithData ,接着看 initWithData

_preloadedLock :这是线程锁,通过信号量控制,为了预加载数据读取安全。 @autoreleasepool :在内部会产生大量的临时变量,为保证内存峰值不会暴涨,使用自动释放池控制 YYImageDecoder :获取图片的一些基本信息,图片宽高、帧数、图片类型 YYImageFrame :此时已经得到了解压缩后的位图

YYImage中使用的是Image I/O进行的图片解码,有两种方式 一、CGImageGetDataProvider(imageRef)方式

CGImageSourceRef :使用 CGImageSourceCreateWithData 函数,根据从磁盘获取的图片二进制数据创建imageSource CGImageRef :使用 CGImageSourceCreateImageAtIndex 函数创建一个CGImage CGDataProviderRef :位图数据提供者, CGImageGetDataProvider 函数,总之就是能从这里拿到位图数据 CFDataRef : CGDataProviderCopyData ,从DataProvider中获取CFDataRef数据。 CGImageRef : CGImageCreate ,根据位图数据再创建一个CGImage。

二、CGBitmapContextCreate方式

这里使用的是 CGBitmapContextCreate 函数进行的解压缩操作

可以看到这个函数有如下参数

通过打印执行这个函数时的线程可以看到,这个操作是 异步执行 的,后面我们会知道这确切来说是 异步串行

YYAnimatedImageView集成UIImageView,并重写了很多方法,先从初始化开始看

初始化时定义了一个 runloopMode 为 NSRunLoopCommonModes ,表示在runloop切换时也要播放动图。

_link :是 CADisplayLink ,计时器,用来播放动画的。 第一次进入 setImage 会先执行 imageChanged 方法来确定图片和容器大小,以及标记定时器,等待下次runloop开始播放动画。

在 resetAnimated 方法中进行了初始化操作,包括 _lock 线程锁、 _buffer 缓存容器的初始化、 _requestQueue 线程队列的初始化以及定时器 _link 的初始化等等。

1、图片渲染到屏幕的过程:从磁盘读取文件->计算Frame->图片解码->通过数据总线提交给GPU渲染->顶点着色器->光栅化处理->片元着色器着色->渲染到帧缓冲区->视频控制器指向帧缓冲区->显示。 2、YYImage避免了全局缓存,在图片显示之前就异步强制图片解压缩,对性能有很大提高,其实这个库还有很多优点,没有再仔细的去看,以后会抽时间看一下。

收录: 原文地址

相似回答