
正文
ios图形开发,ios 图形编程
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
iOS 图形绘制方案
先说下背景,项目里需要绘制音乐和视频的波形图,由于产品上的设计,波形图的长度基本都可以达到屏幕长度的几十倍。并且图形并不是折线图而是柱状图,还要跟随音乐音量变化,所以图形肯定是无法直接拉伸挤压的,所以当时为了性能和内存方面的考虑,尝试了很多方案。
UIImage:
使用UIGraphicsGetImageFromCurrentImageContext方法将绘制的图形生成图片。
这种方式适用于图片不长并且图片不变的情况。
优点:可在子线程绘制,方便缓存。
缺点:占用内存大,绘制不够高效。
PS: 注意此方法有个隐患,因为系统会对设置给UIImageView的图片进行缓存,如果一直调用,即使是完全相同的图片,也会产生内存占用。
示例:
CALayer :
layer的基类,重写drawInContext方法进行进行绘制。
优点:量级轻(实在想不到优点)。
缺点:主线程绘制,绘制不够高效 。
示例:
CATiledLayer:
layer的子类,专门用于绘制大图的方案,系统底层已进行过优化,子线程绘制,并且不会绘制屏幕外的内容。可将大图分割成若干个更小的单元进行绘制,可通过tileSize设置单个绘制单元的大小。
优点:子线程绘制,性能极好。
缺点:绘制较缓慢,能控制的变量较少。
示例:
与CALayer用法一致,可设置额外属性
YYAsyncLayer:
知名的异步绘制的第三方控件,是CALayer的子类,内部创建了队列进行管理,能将绘制的操作转换为异步操作,并且引入了RunLoop机制进行管理,只在RunLoop空闲的时候才进行刷新操作。
优点:子线程绘制,性能很高,不阻塞用户操作。
缺点:因为只在空闲时执行,图形刷新不及时。
示例:
CAShapeLayer + UIBezierPath:
layer的子类,CAshapeLayer能在GPU上渲染,性能很高,绘制速度很快,而且曲线绘制既能选择在主线程绘制,也能选择在子线程绘制。
优点:无论子线程还是主线程都可绘制,且性能很高。
缺点:曲线路径量大的话,还是影响性能。
示例:
最终还是选择了CAShapeLayer + UIBezierPath方案,但是因为音波数据量特别大(每秒40个音频数据),导致多个图形频繁刷新的时候发热十分严重,而且也出现了卡顿的情况。
为了优化,最后又加入了分屏绘制的逻辑:
蓝色区域表示屏幕区域,红色表示绘制的区域,黑色线条表示临界边,
整体逻辑:
0、转换坐标为窗体坐标。
1、判断是否有上次绘制的位置,没有则直接绘制。
2、绘制完成后保存当前位置为绘制位置,计算出黑色临临界区域。
3、滑动视图的过程中判断滑动位置是否超出了黑线区域,超出则重新进行绘制。
4、重复2、3。
相关问答
Q1: 一、iOS相关图形框架初识
自下而上系统的学习或了解iOS中各种图形框架。
OpenGLES → Metal → GPUImage → CoreAnimations → UIKit
CoreImage、CoreGraphics、Quartz2D
参考资料:
OpenGL入门
iOS 图形绘制框架 ——UIBezierPath 、Core Graphics 和OpenGL
iOS图形处理概论:OpenGL ES,Metal,Core Graphics,Core Image,GPUImage,Scene Kit (3D) ,Sprite Kit (2D),OpenCV
关于iOS开发中图片处理的一些积累
Q2: iOS 图形绘制(二)-UIBezierPath
UIBezierPath 可以创建基于矢量的路径,例如椭圆或者矩形,或者有多个直线和曲线段组成的形状。
使用 UIBezierPath ,你只能在当前上下文中绘图,所以如果你当前处于 UIGraphicsBeginImageContextWithOptions 函数或 drawRect: 方法中,你就可以直接使用UIKit提供的方法进行绘图。如果你持有一个 context: 参数,那么使用UIKit提供的方法之前,必须将该上下文参数转化为当前上下文。幸运的是,调用 UIGraphicsPushContext 函数可以方便的将 context: 参数转化为当前上下文,记住最后别忘了调用 UIGraphicsPopContext 函数恢复上下文环境。
简言之:我们一般使用 UIBezierPath 都是在重写的 drawRecrt: 方法这种情形。其绘图的步骤是这样的:
1.重写 drawRect: 方法。但不需要我们自己获取当前上下文 context ;
2.创建相应图形的 UIBezierPath 对象,并设置一些修饰属性;
3.渲染,完成绘制。
绘制矩形最简单的办法是使用 UIRectFrame 和 UIRectFill
通过使用 UIBezierPath 可以自定义绘制线条的粗细,是否圆角等。
多边形是一些简单的形状,这些形状是由一些直线线条组成,我们可以用 moveToPoint: 和 addLineToPoint: 方法去构建。 moveToPoint: 设置我们想要创建形状的起点。从这点开始,我们可以用方法 addLineToPoint: 去创建一个形状的线段。
我们可以连续的创建 line,每一个 line 的起点都是先前的终点,终点就是指定的点。
closePath 可以在最后一个点和第一个点之间画一条线段。
想画弧线组成的不规则形状,我们需要使用中心点、弧度和半径,如下图。弧度使用顺时针脚底,0弧度指向右边,pi/2指向下方,pi指向左边,-pi/2指向上方。然后使用 bezierPathWithArcCenter: radius: startAngle endAngle: clockwise: 方法来绘制。
Q3: iOS开发快速了解OpenGL基本概念
作为一个一个iOS底层开发小白,一直以来对于底层原理,都是一知半解的状态,希望从此时记录自己学习底层知识的过程,也希望对其他开发中可以有一个好的帮助,因为自己看其他人的博客时候,大多数作者都是基于自己认知的前提下,记录自己的知识,在我看来对于入门的人来说,非常晦涩难懂,所以希望自己的文章,可以更加帮助其他人循序渐进的了解更多关于 iOS 的知识。文章也会尽量一步步的探索更多业务开发之外的东西,对于任何方面的技能,都可以友善的帮助后来的开发者。文章中有任何错误,不恰当的地方,欢迎随时指正,多多交流才可以共同进步。想要学习 OpenGL 首先需要了解 OpenGL/OpenGL ES/Metal 三者之间的联系:
通俗来讲就是, Apple 作为大厂,肯定要发展自己的底层渲染技术,所以推出了 Metal ,在推出 Metal 之前,苹果的底层渲染也是基于 OpenGL/OpenGL ES 的,在 iOS 12.0以后,苹果摒弃了 OpenGL 的相关 API ,使用 Metal 作为自己的渲染技术,但是 OpenGL 相关的 API 依然可以使用,因为在 Metal 之前,苹果提供了非常丰富的关于使用 OpenGL 相关 API ,类似于苹果推出了 swift ,但是 OC 依然是自己主流语言一样。了解了相关的背景,我们接下来要知道图形 API 究竟是用来解决什么样的问题存在的。
OpenGL/OpenGL ES/Metal 在任何项目中解决问题的本质就是利用 GPU 芯片来高效的渲染图形图像。图形 API 是 iOS 开发者唯一接近 GPU 的方式。想要了解 OpenGL 就要先学习关于 OpenGL 的专业名词,了解了这些,才可以对以后的学习,有更加深刻的认识。
状态机在 OpenGL 可以这么理解, OpenGL 可以记录自己的状态(当前所使用的颜色、是否开启了混合功能等),可以接输入(当调用 OpenGL 函数的时候,实际上可以看成 OpenGL 在接受我们的输入),如我们调用 glColor3f ,则 OpenGL 接收到这个输入后会修改自己的“当前颜色”这个状态, OpenGL 可以进入停止状态,不再接收输入。在程序退出前, OpenGL 总会先停止工作;
这里有一个 iOS 中很经常听到的概念, 离屏渲染 ,很多人知道离屏渲染会对 APP 的性能造成较大的开销,但是却不知道原理是什么,相信大家了解了上面关于 OpenGL 关于交换缓冲区的概念后,有了一个更清晰的认识,即: Off-Screen Rendering 是需要开辟新的缓冲区的,不停地切换上下文的环境则是对性能的很大的消耗,所以在 iOS 开发中,我们应当尽量的避免离屏渲染。
在学习 OpenGL 的过程中,直接非常直观的掌握并理解这些概念并不是一件容易的事,但是至少需要在入门阶段,大致的了解这些概念的意思,然后通过后续的学习,慢慢的巩固前面学到的知识,温故而知新,一步步的打开关于 iOS 底层渲染知识的大门,学习的越来越深入,慢慢的回过头看以前的知识点的时候,就会豁然开朗了。
希望能一步步的记录自己学习的过程,慢慢进步,慢慢成长,文章中有任何错误的地方,欢迎指正。希望能多多交流,共同进步。
Q4: 四、iOS中图形图像渲染技术栈及流水线
下图为 iOS APP 图形渲染框架, APP 在显示可视化的图形时,使用到了 Core Animation 、 Core Graphics 、 Core Image 等框架,这些框架在渲染图形时,都需要通过 OpenGL ES / Metal 来驱动 GPU 进行渲染与绘制。
UIKit 是 iOS 开发者最常用的框架,里面提供了 UIView 。
UIView 供开发者用来:
Core Animation 源自于 Layer Kit, 是一个复合引擎,主要职责包含渲染( CALayer )、构建和实现动画。 CALayer 是用户所能在屏幕上看到一切的基础。
Core Graphics 是基于Quartz 的高级绘图引擎,主要用于运行时绘制图像。其功能有绘制路径、颜色管理、渐变、阴影、创建图像、图像遮罩、PDF文档创建显示及分析。
Core Image 拥有一系列现成的图像过滤器,可以对已存在的图片进行高效处理。大部分情况下,``Core Image ``` 是在GPU中完成工作,如果GPU忙,会使用CPU进行处理。
Core Animation 、 Core Graphics 、 Core Image 这个三个框架间也存在着依赖关系。
上面提到 CALayer 是用户所能在屏幕上看到一切的基础。所以 Core Graphics 、 Core Image 是需要依赖于 CALayer 来显示界面的。由于 CALayer 又是 Core Animation 框架提供的,所以说 Core Graphics 、 Core Image 是依赖于``Core Animation ```的。
上文还提到每一个 UIView 内部都关联一个 CALayer 图层,即 backing layer ,每一个 CALayer 都包含一个 content 属性指向一块缓存区,即 backing store , 里面存放位图(Bitmap)。 iOS 中将该缓存区保存的图片称为 寄宿图 。
这个寄宿图有两个设置方式:
CALayer 是如何调用 GPU 并显示可视化内容的呢?下面我们就需要介绍一下 Core Animation 流水线的工作原理。
事实上,app 本身并不负责渲染,渲染则是由一个独立的进程负责,即 Render Server 进程。
App 通过 IPC 将渲染任务及相关数据提交给 Render Server 。 Render Server 处理完数据后,再传递至 GPU。最后由 GPU 调用 iOS 的图像设备进行显示。
Core Animation 流水线的详细过程如下:
对上述步骤进行串联,它们执行所消耗的时间远远超过 16.67 ms,因此为了满足对屏幕的 60 FPS 刷新率的支持,需要将这些步骤进行分解,通过流水线的方式进行并行执行,如下图所示。
在 Core Animation 流水线中,app 调用 Render Server 前的最后一步 Commit Transaction 其实可以细分为 4 个步骤:
参考文章: iOS 图像渲染原理







