RenderObject 作为所有Render 树节点的基类,完全类似与DOM树中的Node基类,它是构成Render 树的基础,作用非比寻常,其中包含了构成Render 树所可能涉及到的一些基本属性及方法,内容相当多,其主要数据成员及方法分别如下:
其中成员m_parent、m_previous、m_next为构建Render 树设置好关联基础;m_Node 则为DOM 树中对应的节点;m_style 成员则描述该节点对应的各种CSS基本属性数据,下面会单独介绍;至于其他的诸如m_positioned、m_isText、m_inline、m_floating、m_replaced等则描述其特性,就像CSS标准对不同元素的属性分类定义一样,从字面上我们就可以上一节WebKit 网页布局实现之基本概念及标准篇中可以找到它们这么定义的踪影。成员m_needsPositionedMovementLayout 、m_normalChildNeedsLayout 、m_posChildNeedsLayout 、m_needsLayout 等主要用来描述该RenderObject 是否确实需要重新布局;当 一个新的RenderObject 对象插入到Render 树的时候,它会设置其m_needsLayout 属性为true,同时会根据该 RenderObject对象在祖先RenderObject 看来是一个positioned( 拥有positiong:absolute 或fixed 属 性)状态的孩子,如是则将相应祖先RenderObject 对象的属性m_posChildNeedsLayout 设置为true;如果是一个in-flow(positon:static 或relative)状态的孩子,则将相应祖先RenderObject对象的属性m_normalChildNeedsLayout设置为true;
其中很多方法如paint()、layout()等是虚拟的,不同的子类可以重载它;其中方法container() 、containingBlock()、paint()、layout()很值得大家深入研究;总的说来RenderObject 基类定义一些通用属性、方法,以便维护、布局、渲染Render 树。
1.RenderObject创建过程
2.RenderObject继承关系
2 子类RenderBox
RenderBox 代表描述CSS 标准中的Box Model,它继承自RenderObject,其主要重载了部分继承而来的方法。
3.子类RenderContainer
RenderContainer 类用来描述可以拥有子RenderObject 成员的容器类,它继承自RenderBox。其主要重载了RenderObject 提供的维护Render 树新增、删除树节点等方面的方法
4.子类RenderFlow
RenderFlow 主要用来描述CSS 标准中提到的能进行inline-flow、block-flow 相关处理的Render 树结点,它继承自RenderContainer;其主要方法包括在flow的过程中创建、关联匿名
对象等
5.子类RenderBlock
RenderBlock 代表CSS 标准中的block-level 元素,它继承自RenderFlow它维护了一组由它定位的positioned 树节点,以及有关overflow 方面的设置。其主要重载了RenderObject 继承下来的layout、paint等方法。因为html中的body、div、p等标签对应RenderBlock 类对象,其在Render 树具有非常重要的地位,其layout、paint 等方法的实现,往往是WebKit 整个布局、渲染处理的发起中心,内容比较多并且复杂,以后有机会详解。
6.子类RenderInline
RenderInline 代表inline-level 元素, 其继承自RenderFlow,主要重载了RenderObject 关于inline-flow 方面处理的方法,提供了splitFlow、splitInlines等处理自动换行的方法。
7.子类RenderText
RenderText 代表对html 中Text node 对应的Render 树节点,它直接继承自RenderObject它提供关于处理文字方面如显示文字、行高计算、整个Text node对应的宽度等;它没有重载layout 方法,因为它自身的定位往往由
RenderBlock、RenderInline 父对象来处理;
8.子类RenderImage
RenderImage 代表html 中img 标签对应的树节点,它继承自RenderBox。其主要提供关于图片显示、大小设置等方面的处理,其中paintReplaced 方法将其图片显示出来;
9.子类RenderView
RenderView对应整个html文档对象的树节点,可看成是Render树的根,它继承自RenderBlock其中m_frameview 成员对应整个文档对应的FrameView,而m_widgets 则包括了该文档可能包含的plugin 插件等对应的Render
树节点。RenderView 对象作为Render 树的根,它往往随着Document 对象的创建而创建,它的layout、paint 方法的发起往往是整颗Render树布局、渲染处理的开始。其中也包含了对选择处理
10.子类RenderButton
RenderButton代表html中input标签type为button时对应的Render 树节点,它直接继承自RenderFlexibleBox。RenderFlexibleBox代表能按居中、左对齐、右对齐等水平或垂直方向布局子节点的树节点
其中m_buttonText为button上的文字对应的树节点,而m_inner为添加m_buttonText 时创建的匿名对象,以便于居中等处理等。这成员的创建来自于方法updateFromElement;
这 些css属性通过CSSStyleSelector::applyProperty方法来设定其成员m_RenderStyle 对应的值, 其中包含m_style->setAppearance(PushButtonAppearance)。尤其值得关注,其初步决定了button是如何画出来的
11.子类RenderTextControl
RenderTextControl 代表html 中input 标签type 为text 或textarea标签对应的Render 树节点,它直接继承自RenderBlock;其中成员m_multiLine 以描述是textarea 或text input;m_innerText 为其中包括的文字对应的树节点;当作搜索按钮时m_cancelButton/m_resultsButton 为对应的树节点;这些成员的创建来自于方法updateFromElement;
12.子类RenderListBox
RenderListBox代表html中select标签对应的Render树节点,它直接继承自RenderBlock;
13.子类RenderTheme
RenderTheme 在html 标签中没有对应的页面元素,其作用主要用于如何渲染按钮、输入框、列表框等,其实现往往有一定平台相关性。RenderTheme 往往提供一个接口,不同的图形库对其中不同的方法如paintbutton、paintcheckbox、painttextfield等进行了实现;其 中 paint 方法则根据appearance 属性的不同以分别调用不同的paintxxx 方法,
14. 子 类 RenderTable 、 RenderTableRow 、 RenderTableCol 、RenderTableCell
RenderTable 主要通过addChild 方法来维护对RenderTableCell、RenderTableCol、RenderTableRow等对象的管理.RenderTableRow 通过方法layout 和paint 方法来布局管理RenderTableCell对象。这一组子类主要实现人们熟知的表格布局,
15.子类RenderFrame
RenderFrame代表html中标签frame对应的Render 树节点其中属性m_widget、m_view 代表frame 对应的widget 及frameview,通过其中setwidget 方法来设置m_widget 属性,m_view属性则在对象创建的时候设置为当前document对应的frameview。其中html 中的embed/object 插件标签对应的Render 树节点为RenderPartObject对象。
3.渲染对象(RenderObjects)与渲染图层(RenderLayers)
每个渲染对象(RenderObject)都有其对应的渲染图层(RenderLayer),或者是直接关联,或者是通过其根对象(其根级RenderObject)间接关联。
一般来讲,拥有相同坐标空间(如受控于相同的CSS变换)的渲染对象会属于同一个渲染图层。渲染图层可以保证渲染对象在重叠、半透明等情况时,能以正确层叠次序进行绘制。
RenderBoxModelObject::requiresLayer()中定义了一些为渲染对象创建新渲染图层的前提条件,如:
* 渲染对象是页面的根对象;
* 渲染对象有独立的CSS定位属性(相对、绝对、变换);
* 渲染对象是透明的;
* 覆盖有透明(Alpha)蒙版或者反射层;
* 有CSS效果滤镜;
* 该渲染对象对应于<canvas>标签指定的WebGL或者2D加速图形元素;
* 该渲染对象对应于<video>标签元素;
注意渲染对象与渲染图层间不存在一对一关系,一个渲染对象,要么对应于专为它创建的的渲染图层,要么对应于其某级根对象的渲染图层。
渲染图层(RenderLayers)也是以树形结构来组织的,根部的图层即对应渲染树(Render Tree)中根部的渲染对象。
每个渲染图层(RenderLayer)同时存储在两个基于不同深度值方向(Z-Order)的列表中:正向深度列表posZOrderList,以及负向深度列表negZOrderList。
用WebGL技术的canvas元素。每个合成层都有一个RenderLayerBacking,RenderLayerBacking负责管理RenderLayer所需要的所有后端存储,因为后端存储可能需要多个存储空间。在WebKit中,存储空间使用类GraphicsLayer来表示。
下图就是WebKit构建的从RenderLayer树到RenderLayBacking树,再到GraphicsLayer树,并给出这些硬件加速基础设施的对应关系。在RenderLayer树中的第四个节点没有创建RenderLayerBacking对象,因为不符合上面的创建条件,而对于每个RenderLayerBacking对象,它也至少需要一个GraphicsLayer对象,当然也可能需要多个,图中的RenderLayerBacking分别需要2个,1个或者4个GraphicsLayer对象,这些对象分别表示什么呢?
为什么一个RenderLayerBacking对象需要这么多层呢?原因有很多,例如是需要将滚动条独立开来称为一个层,需要前景层、背景层等,需要两个容器层来表示RenderLayer对应的Z坐标为正数的子女和Z坐标负数的子女,将可能需要滚动的内容建立新层,还有剪裁层和反射层
4. 三个树结构
可以说,渲染过程中,有三个树形结构各司其职:
* DOM树(DOM Tree),持有构成页面的所有的数据节点对象;
* 渲染对象树(RenderObject Tree),持有与可显示节点(Node)一对一的渲染对象,每个渲染对象知道如何绘制与其对应的节点;
* 渲染图层树(RenderLayer Tree),基于渲染对象树构建的层级树,每个渲染图层对应一至多个渲染对象;
5. 软件渲染过程
WebKit在渲染页面时,从根节点开始遍历渲染图层树(RenderLayer Tree),直至叶子节点,在页面渲染的实现上,WebKit包含两种方式:
软件渲染和硬件加速渲染,而软件渲染是最基本的方式。
软件渲染过程,整个页面中的每个渲染图层按照深度从后向前的顺序依次被绘制,渲染图层的主要绘制工作都是在RenderLayer::paintLayer()中完成,其中大概包含如下步骤:
a. 判断图层边界是否与刷新矩形有交叉;
b. 通过调用图层的paintLayer()方法,屏幕深度上从前向后,递归绘制negZOrderList列表中位于该图层后面的图层;
c. 让该图层相关联的所有渲染对象(RenderObject)完成各自的绘制;
d. 渲染对象(RenderObject)的绘制过程,从那个创建该图层的对象开始,向渲染对象树(RenderObject Tree)末端遍历,
直至遇到一个不属于当前渲染图层(RenderLayer)的渲染对象(RenderObject)而结束;
e. 通过调用图层的paintLayer()方法,屏幕深度上从后向前,递归绘制posZOrderList列表中位于该图层前面的图层。
在这种渲染方式下,渲染对象通过对绘图环境(GraphicsContext,Chrome中的Skia库)执行一系列绘图操作,将自己的内容绘制在内存位图上。
绘图环境本身并不关心图层的概念,但是在绘制半透明图层时有一点需要注意:半透明图层在开始绘制渲染对象时,会调用GraphicsContext::beginTransparencyLayer(),
在Skia库的实现中,这个调用会使得后续的所有绘制都在独立的位图上进行,以至于当该图层相对应的所有对象渲染完成时(此时endTransparencyLayer()方法被调用),
这些独立位图能够与基础图像(或者称背景图像)进行合成(进行必要的Alpha混合,以实现半透明效果)。
6. 将WebKit渲染结果显示在屏幕上
当所有的渲染图层(RenderLayers)将内容绘制到内存位图上之后,还需要将内存位图呈现在屏幕上。在Chrome系统中,渲染器进程会将渲染好的内存位图放入共享内存中,
然后通过IPC消息通知浏览器进程进行界面更新,浏览器进程收到IPC通知后,使用操作系统API,将内存位图绘制到指定窗口(标签对应的网页视图)上。