关于flutter:深入理解Flutter的图形图像绘制原理图形库skia剖析

本文来自OPPO互联网技术团队,转载请注名作者。同时欢送关注咱们的公众号:OPPO_tech,与你分享OPPO前沿互联网技术及流动。

Flutter是目前风行的高性能跨平台UI框架,图形库skia是其跨平台的基石。本文将深入分析skia的图形、字体、图片的渲染原理,如何开掘硬件个性,为UI性能优化提供思路。

1. 引言

Flutter是目前十分风行的跨平台UI开发框架,不仅反对Android、iOS,还反对Windows、Linux等操作系统。Flutter的性能十分高,领有120fps的刷新率。那么flutter是如何实现在不同平台上高性能绘制图形图像的呢?首先咱们剖析下Flutter App和原生Android App、原生iOS App的UI绘制原理。

挪动App的整体UI框架大抵分成上面4个档次:

1)UI库

跟Android、iOS原生开发相似,Flutter用dart语言实现一整套UI控件。Flutter先将控件树转成渲染树,而后交由skia库绘制界面。

2)图形库

Skia图形库跟iOS平台的CoreAnimation等库性能相似,不仅提供了图形渲染性能,还提供文字绘制和图片显示性能。高级图形图像库将须要绘制的图形转成点、线、三角形等图元,再调用底层图形接口实现绘制。

3)低级图形接口

OpenGL是应用最广的低级图形接口,兼容性最好,基本上反对市面上的所有GPU。Vulkan是最近几年新推出的图形API,除了iPhone的GPU,其余厂家的GPU根本都反对。Metal是苹果新推出的图形API,只反对自家GPU。

4)硬件设施层

目前的挪动设施出于性能思考,大部分图形都是通过GPU渲染,多数状况也会应用CPU渲染,后文会介绍skia应用CPU和GPU渲染的具体场景。

iPhone 在A11芯片以前应用power vr系列GPU,之后采纳自研GPU。安卓手机大部分采纳高通Adreno GPU或ARM mail GPU。GPU渲染完一帧图像后送FrameBuffer,最初在适合的机会展现在屏幕上。

Skia利用宽泛并且跨平台,不仅用于Flutter和Android操作系统,还用于Google Chrome浏览器,同时反对windows、Mac、iOS操作系统。Skia由C++编写,代码开源,通过钻研skia有助于了解图形图像的绘制原理,为UI性能优化提供思路。

2. skia 框架剖析

2.1 Skia内部组件依赖

Skia依赖的第三方库泛滥,包含字体解析库freeType,图片编解码库libjpeg-turbo、libpng、libgifocode、libwebp和pdf文档解决库fpdfemb等。Skia反对多种软硬件平台,既反对ARM和x86指令集,也反对OpenGL、Metal和Vulkan低级图形接口。

2.2 Skia 档次剖析

Skia在结构上大抵分成三层:画布层,渲染设施层和封装适配层。

2.2.1 画布层

画布层能够了解成提供给开发者在一个设施无关的画布,能够在下面绘制各种图形,且不必关怀硬件细节,性能如下:

类别 函数名 含意
画图形 drawPoints 画点
画图形 drawRect 画矩形
画图形 drawVertices 画多边形
画图形 drawRoundRect 画圆角矩形
画图形 drawArc 画圆弧
画图形 drawOval 画椭圆
画图形 drawPath 画矢量图
绘制文字 drawText 显示文字
显示图片 drawBitmap 显示位图

2.2.2 渲染设施层

渲染设施层负责画布层的硬件实现,skia将设施封装成上面三个类:

1)SKBitmapDevice

CPU渲染模式绘图,用于没有显卡或者显卡驱动的设施。此模式下,最初会将须要绘制的图形转成位图数据(RGB)写入指定内存,故称为BitmapDevice。写内存操作通过AVX或者NEON指令集实现。

2)SKGPUDevice

GPU渲染形式绘图。目前大部分挪动设施和个人电脑都有GPU,GPU比CPU的运算单元多,并行计算能力强,通过GPU绘图可升高CPU占用,性能更好。Flutter、最新版本的chrome和android零碎默认设置为GPU渲染模式。Chrome中的配置截图如下,可看到默认采纳GPU渲染。

3)SKPDFDevice

选用此设施时,渲染后果不是输入到显示器的画面,而是输入为pdf文件。

能够通过skia官网在线体验不同设施的渲染后果:https://fiddle.skia.org/c/@sh…

2.2.3封装适配层

Skia为了屏蔽不同依赖库的接口差别,对依赖库进行了封装和适配。例如基于图片编解码库libjpeg-turbo、libpng、libwebp 封装了类SKJpegCodec、SKPngCodec、SKWebpCodec。基于底层图形库OpenGL、Metal、Vulkan封装了GrGLOpsRenderPass, GrMTOpsRenderPass, GrVKOpsRenderPass三个类。基于苹果平台CoreText字体库和开源字体FreeType封装了类SkScalerContext_Mac和SkScalerContext_FreeType。

Skia的内部依赖和层级构造解说结束,上面重点解说skia的图形、文字和图片的绘制原理。

3. 图形绘制原理

Skia反对绘制的图形泛滥,包含圆形、椭圆、矩形、贝塞尔曲线等。下文重点剖析图形的CPU和GPU两种渲染模式的原理。

3.1 图形CPU渲染原理

曲线的绘制波及的数学知识较多,本文不再开展,上面以绘制实心矩形为例阐明原理进行分析。

1)调用画布的绘图API

应用层调用画布SKCanvas的drawRect函数,传入左上角和右下角顶点坐标。

2)选用对应的设施的绘图API

因为抉择的是CPU渲染模式,故调用SKBitmapDevice的矩形绘图函数drawRect实现。

3)图形示意

所有的图形可分解成上面几种根本矢量图形的组合,矩形可示意成四条直线的组合。

曲线类型 参数 用处
直线(一次贝塞尔曲线) 终点坐标,起点坐标 可示意绘制三角形、四边形等多边形
圆锥曲线 终点坐标,起点坐标,椭圆参数 示意椭圆、圆弧、圆形
二次贝塞尔曲线 三个控制点 示意TrueType字体、抛物线等曲线
三次贝塞尔曲线 四个控制点 示意OpenType字体和其余曲线

4)绘制算法实现

矢量图转成位图的过程称为光栅化。带填充的矩形光栅化过程比较简单,能够分解成绘制多条横线。

5)横线线绘制算法

每条横向的画法通过SKBlitter:: blitH实现。接口定义如下:

virtual void blitH(int x, int y, int width);

性能:从坐标x,y开始,间断写入宽度为width的RGB色彩值。

6)内存中写色彩数据

通过追踪代码,发现上文中的横线绘制函数调用的是memsetT函数(内存赋值)实现。参数如下:

static void memsetT(T buffer[], T value, int count)

目前x86和ARM处理器是32或者64位,一般的指令一次最多写入32位 或者64位数据,一个带通明通道的点通常占4个字节,相当于一次只能绘制1到2个点,效率比拟低。Skia从性能角度思考,采纳的SIMD指令集来减速内存操作。

在X86平台,调用SSE、AVX、AVX2等指令集实现内存赋值,SSE反对一次操作128位操作,AVX/AVX2反对一次操作256位数据,ARM处理器的NEON指令集反对一次操作128位数据。

3.2 图形GPU渲染原理

GPU的并行运算能力强,目前大部分挪动设施都采纳的是GPU渲染。

skia GPU渲染流程如下:

1)发动绘图,先调用SKCanvas的绘图函数drawRect,传入左上角和右下角顶点坐标。

2)调用GPU设施的绘图函数SKGPUDevice::drawRect。

3)采纳命令模式,将GPU绘图操作封装成类GrOpsTask的实例。

4)依据软硬件平台的不同选用不同的底层API。

OpenGL(Open Graphics Library”)是目前应用最宽泛的跨平台图形变成接口,跨平台个性好,大部分操作系统和GPU。Skia在大部分平台采纳OpenGL实现GPU绘图,少部分平台调用Metal和vulkan。

Metal是苹果公司2014年推出的和 OpenGL相似的面向底层的图形编程接口,只反对iOS。对软硬件有要求,要求硬件苹果A7及当前,操作系统iOS 10及以上。Metal实践上性能比OpenGL性能强,故新设施中开启Metal可进步性能。例如Flutter中已启用了metal反对,详情参考https://github.com/flutter/fl…。

Vulkan是新一代跨平台的2D和3D绘图利用程序接口(API),旨在取代OpenGL,实践上性能强于OpenGL。自 Android 7.0 开发者预览版开始,Google便在零碎平台中增加了对Vulkan的API反对。目前Skia的GPU渲染模式已用vulkan实现了一套,但存在一些bug。具体参考https://skia.org/user/special…。

Skia对上述三种图形接口进行了封装,屏蔽了不同底层图形API接口的差别。OpenGL接口的封为GrGLOpsRenderPass,Metal的封装层为GrMTOpsRenderPass,Vuklan的封装层为 GrVKOpsRenderPass。

5)通过GPU实现残余绘图操作。

上面以OpenGL为例阐明。接口封装层调用OpenGL glDrawArray绘制矩形,之后在渲染流水线中实现顶点变换、光栅化和着色,最初送帧缓冲显示。渲染流水线如下图所示:

Metal、vulkan的渲染流水线这里不再开展。

4. 字体绘制原理

字体无奈间接显示在屏幕上,须要解析成位图或者矢量图能力绘制。Skia的字体解析实现跟进平台差别有所不同,mac和iOS平台调用coreText库,安卓平台调用开源库freeType。

FreeType是一个用C语言实现的,收费的高质量可移植字体引擎,反对点阵字体PCF、BDF和矢量字体TrueType、freeType等字体。

4.1 skia点阵字体绘制原理

Skia反对的点阵字体有PCF、BDF格局。点阵存储的是多张位图,常见的有1616,2424,32*32等尺寸,解码和显示简略,毛病是放大后有锯齿。

1) skia点阵文字显示代码:

SkFont font;
font.setEdging(SkFont::Edging::kAlias);
font.setSize(40);
const char text[] = "Click this link!";
size_t byteLength = strlen(static_cast<const char*>(text));
canvas->drawSimpleText(text, byteLength, SkTextEncoding::kUTF8, x, y, font, SkPaint());

文字绘制流程如下:

点阵字体最初解析成了位图,而后依据平台不同选用CPU或者GPU渲染进去。Skia为了进步字体显示速度,对字体的解析后果做了内存缓存。

4.2 矢量字体绘制原理

矢量字体次要通过贝塞尔曲线形容字体,存储空间小,但渲染简单,还须要导入字体库文件。Skia反对的矢量字体有tff(true type font)和otf(open true type)格局。前者采纳二次贝塞尔曲线示意,后者采纳三次贝塞尔曲线示意。Skia中矢量文字绘制代码如下:

SkPaint p;
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(10);
    p.setARGB(0xff, 0xbb, 0x00, 0x00);
   sk_sp<SkTypeface> ttf = MakeResourceAsTypeface("fonts/Stroking.ttf");
SkFont font(ttf, 100);
if (ttf) {
        SkFont font(ttf, 100);
        canvas->drawString("○◉  ⁻₋⁺₊", 10, 100, font, p);
}

绘制流程如下:

矢量字体的绘制流程跟点阵字体大部分一样,不同之处在于解析后果为贝塞尔曲线。贝塞尔曲线的渲染算法略微简单,参考文章https://www3.cs.stonybrook.ed…

5. 图片绘制原理

5.1 Skia位图绘制原理

skia提供了showBitmap函数可间接显示位图。位图渲染模式跟矢量图形相似,分为CPU渲染和GPU渲染。位图的CPU渲染跟实心矩形的渲染原理相似,通过SIMD指令集将位图内存一行一行拷贝到指定内存缓存中。GPU渲染模式通过调用OpenGL、Metal、vulkan的纹理贴图函数实现。

5.2 Skia压缩格局图片绘制原理

位图因为占用空间大,应用频率低,大部分状况下应用压缩格局图片。Skia反对的压缩格局图片如下:

格局 长处 毛病 场景 依赖解码库
gif 文件小,反对动画、通明,无兼容性问题 只反对种颜色,且透明度只有1位,有白边和锯齿 简略的动图 libgifcodec
jpg 反对位真彩色,压缩率高 有损压缩,不反对通明通道 色调丰盛的图片 libjpeg-turbo
png 无损压缩,反对通明,简略图片尺寸小 不反对动画,压缩率低 logo/icon/透明图 libpng
webp 比jpeg压缩率更高,反对有损和无损压缩,反对动画、通明通道 谷歌自研格局,局部平台不反对。 反对有损和无损压缩格局,反对动画 libwebp

压缩格局图片应用代码如下:

SkCanvas c(dst);  
    SkBitmap src;  
    SkImageDecoder::DecodeFile(“test.jpg”, &src);//  图片解码
    c.drawBitmap(src, 0, 0, NULL);  //图片显示

显示流程如下图所示:

读取文件后,先通过文件头判断图片类型,而后送相应的图片库解码成位图图像后,再通过CPU或者GPU渲染。

6. skia小结

Skia是一个功能强大的跨平台图形库,能绘制矩形、圆形、贝塞尔曲线等矢量图,绘制点阵字体和矢量字体,显示jpeg、png、gif、webp等图片,同时性能好,从算法和硬件两个层面进行了优化。skia反对多种软硬件平台,除了Android、chrome、Flutter等产品间接将其作为图形引擎,也反对iOS、windows等操作系统。Skia性能较多,还反对lottie动画,图像特效,还引入了两头语言SKGL,限于篇幅,这里不再开展。

参考文档:

iOS高性能绘图:https://medium.com/@almalehde…

Core Animation 编程指南:https://developer.apple.com/l…

skia编译办法:https://skia.org/user/build

Skia技术路线:

https://docs.google.com/docum…

SKGL阐明:https://github.com/google/ski…。

Skia源码:https://skia.googlesource.com…

Skia 百科:https://zh.wikipedia.org/zh-c…

字体介绍:http://www.klayge.org/wiki/index.php/%E5%AD%97%E4%BD%93%E7%B3%BB%E7%BB%9F

FreeType官网:https://www.freetype.org/

png压缩原理:https://www.jianshu.com/p/5ad…

GPU渲染流水线:https://zhuanlan.zhihu.com/p/…

Vukan介绍:https://www.khronos.org/asset…

ARM Mali GPU介绍:https://developer.arm.com/sol…

Vulkan和OpenGL ES比拟:https://community.arm.com/dev…

Qualcomm发表Adreno 530 GPU反对vulkan:https://www.qualcomm.cn/news/…

https://www.adobe.com/content…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理