1.[译] 如何自定义React渲染器-Hello World
2.Echarts-ZRender源码分析(一)
3.FFMPEG源码学习(一) 参数的渲染解析
4.UE4-Slate源码学习(六)slate渲染Part2-Paint控件绘制
[译] 如何自定义React渲染器-Hello World
本文详细阐述了如何创建一个基本的自定义React渲染器,并通过Hello World示例进行演示。器源首先,码渲码创建一个React项目,染代并在其中实现一个极简的渲染DOM渲染器。接着,器源缠绕公式源码为了展示自定义渲染器的码渲码使用,将实现一个简单的染代计数器组件,并在其中替换默认的渲染ReactDOM渲染器。
在App.js文件中添加计数器组件,器源展示基本功能。码渲码
在index.js文件中,染代使用自定义的渲染渲染器MyCustomRenderer替换默认的ReactDOM渲染器。确保引入react-reconciler包,器源通过yarn add react-reconciler完成安装。码渲码
创建myCustomRenderer.js文件,实现渲染器的核心功能,主要为render函数,使用react-reconciler作为支持。
在index.js中,使用MyCustomRenderer.render方法替换ReactDOM.render,接收三个参数:ReactElement、domElement和回调函数,其中回调函数在本示例中被忽略。
引入react-reconciler包后,需配置hostConfig,在线登记源码包含回调函数,如getRootHostContext、supportsMutation等。其中,getRootHostContext函数作为hostConfig的一部分,用于维护实现renderer所需信息。
根据错误提示,补充hostConfig中空方法,如支持UI树操作、UI更新、prop更新等。
完成以上步骤后,即可实现一个基本的自定义React渲染器,满足Hello World级别的应用需求。该自定义渲染器简单实用,为后续复杂应用的渲染提供基础框架。完整源码可于GitHub仓库查看。
Echarts-ZRender源码分析(一)
Echarts的底层图形绘制引擎ZRender,是一个独立的2D图形绘制引擎,支持Canvas/SVG(5.0后不再支持VML)。它具备图形绘制、管理(包括CRUD操作和组管理)、图形动画和事件管理(在Canvas中实现DOM事件)、响应式帧渲染以及可选渲染器功能。
ZRender的MySQL源码封装架构遵循MVC模式,分为视图层、控制层和数据层。视图层负责图形渲染,控制层处理用户交互,数据层负责数据模型的管理和存储。此外,还包含辅助功能模块,如图形和Group的管理,其中图形特指2D矢量图形。
源码文件结构清晰,入口文件zrender.ts中定义了全局方法,如初始化、删除等操作,ZRender类则负责核心功能的实现。通过实例化代码展示,可以看到如何绘制一个px的圆形并绑定动画,ZRender会处理绘制流程,并将动画添加到管理器中生成帧,开始动画绘制。
后续章节将深入解析元素对象、事件管理器、动画管理器和渲染器的源码。作者雷庭,北京优锘科技前端架构师,有年前端开发和架构经验,yy会员源码专注于可视化前端开发,有兴趣交流的朋友可通过微信ltlt联系他。
FFMPEG源码学习(一) 参数的解析
本文探讨ffmpeg源码学习中的参数解析部分。我们以ffmpeg版本4.4,运行平台为WSL2 ubuntu.为例。 ffmpeg的参数结构主要由三部分组成,具体解析流程在main函数中启动。 解析参数工作由ffmpeg_parse_options函数负责,其核心操作由split_commandline实现。 split_commandline方法在解析参数时通过while循环进行,循环逻辑分为六个步骤:检查是否出现“--”情况。
如无"-*"参数,直接作为输出参数。
若输入参数符合groups中的sep内容,加入输入参数中。
对于非输入输出参数,视为一般参数处理,如"-f"。
搜索设备、解码器、渲染器等中与当前参数相关的元素,并存入AVDictionary。
查找与"--no"类型参数关联的内容。
至此,参数解析基本完成,iapp官方源码ffmpeg_parse_options随后执行进一步操作。UE4-Slate源码学习(六)slate渲染Part2-Paint控件绘制
上一篇文章介绍了绘制一个SWindow的初期步骤,即计算整个UI树的控件大小,为绘制做准备。文章随后深入探讨了绘制流程的第二步,即执行FSlateApplication::PrivateDrawWindows()后,开始调用SWidget::Paint()函数,每个控件随后实现其虚函数OnPaint()。
在这一过程中,绘制参数被封装在FPaintArgs中,作为Paint和OnPaint过程中的关键引用参数。FSlateRHIRenderer与FSlateDrawBuffer是继承自FSlateRenderer的类,作为FSlateApplicationBase的全局变量,在构造时创建。在绘制过程中,通过GetDrawBuffer()函数可获取到FSlateDrawBuffer对象。
FSlateDrawBuffer实现了Slate的绘制缓冲区,内部封装了FSlateWindowElementList数组,用于存储多个SWindow下的绘制元素列表。每个SWindow通过AddWindowElementList()返回一个元素列表。
FSlateWindowElementList负载了SWindow内的所有图元信息,内部封装了FSlateDrawElement的数组,包含Cached和Uncached元素,以及SWindow的指针和用于渲染的批处理数据FSlateBatchData。
FSlateDrawElement是构建Slate渲染界面的基本块,封装了UI树节点控件需要渲染的相关信息,如渲染变换、位置、大小、层级ID、绘制效果等,以及后续渲染阶段需要的相关数据。
在Paint流程中,处理当前传入的SWindow和ChildWindows,首先判断窗口是否可见和是否最小化,然后从参数封装的OutDrawBuffer中获取WindowElementList。调用SWindow的PaintWindow()函数开始绘制窗口,并最终返回所有子控件计算完的最大层级。接着,子窗口递归绘制。
PaintWindow()函数在绘制窗口时,首先调用SetHittestArea()设置点击区域,HittestGrid会判断窗口大小是否改变,若不变则仅更新窗口在屏幕中的位置。构造FPaintArgs参数后,将其封装到FSlateInvalidationContext中。
FSlateInvalidationRoot类的PaintInvalidationRoot()函数可以作为控件树的根节点或叶子节点(SInvalidationPanel),构建快速路径避免每次绘制都计算大小和Paint函数,有利于优化。本篇文章主要分析正常慢速路径调用流程,优化相关将另文分析。
PaintSlowPath()函数从SWindow开始调用Paint()函数,并定义LayerId从0开始作为参数,进行实际的绘制相关计算。
Paint()函数首先处理裁剪、透明度混合、坐标转换等代码。若SWidget包含NeedsTick掩码,则调用Tick函数,我们在日常开发中通过蓝图或lua使用Tick函数时即调用到这里,通过SObjectWidget::Tick调用到UUserWidget::NativeTick供实现Tick。构造FSlateWidgetPersistentState PersistentState作为SWidget的变量,表示Paint时的状态。
PersistentState.CachedElementHandle将当前SWidget存储到FSlateWindowElementList中的WidgetDrawStack数组中。
更新FPaintArgs中的父节点参数和继承可点击测试参数,判断点击测试状态,然后将当前SWidget添加到点击测试中。调用虚函数OnPaint,由控件自己实现。
OnPaint()函数参数包括绘制参数引用、几何体、裁剪矩形、缓冲元素列表、层级、控件风格、父节点状态等。最后处理重绘标签、延迟绘制相关内容、UpdateWidgetProxy()根据缓存句柄更新快速路径中需要处理标记设置为Volatile不稳定状态的SWidget。
虚函数OnPaint()由子类自己实现,本文列举了SImage、SButton、SCompoundWidget和SConstraintCanvas的OnPaint()示例代码学习。
在SImage中,简单判断Brush是否存在以及BrushDrawType的类型,然后调用FSlateDrawElement::MakeBox将控件添加到缓冲区元素列表中。
SButton继承自SCompoundWidget,GetBorder()根据当前按钮状态返回ui中设置的Enabled、Press、Hover、Disabled等状态的Brush。
SCompoundWidget作为合成节点,有且只能有一个子节点,且在Paint时强制将子节点的LayerId+1,同时SCompoundWidget可以单独设置混合颜色和透明度,影响子节点。
SConstraintCanvas作为SWidget的基类对应UMG中常用的UCanvasPanel,通过ArrangeLayeredChildren()对孩子进行层级排序,并根据孩子的层级是否相同存储bool值在ChildLayers中。遍历所有孩子,判断是否开启新层级,递归调用Paint函数,最后返回最大层级。
SConstraintCanvas::ArrangeLayeredChildren函数中,获取设置bExplicitChildZOrder,表示可以将同层一次渲染,有利于提高渲染器批处理。对所有孩子排序,排序规则为FSortSlotsByZOrder。遍历所有孩子,判断可见性掩码、计算偏移、锚点、位置、拉伸缩放等,封装成FArrangedWidget存储到ArrangedChildren中,用于OnPaint时有序遍历。判断每个孩子ZOrder是否相同,相同则bNewLayer为false,大于LastZOrder则将bNewLayer设置为true,最终存储到ArrangedChildLayers中,用于OnPaint函数判断是否将layerId+1。
FSlateDrawElement::MakeBox()函数在OnPaint之后调用,将绘制控件的相关信息通过创建FSlateDrawElement绘制元素对象,添加到SWindow管理的FSlateWindowElementList元素列表中。创建Payload用于存储贴图等相关信息,根据控件Paint过程中的参数调用Element.Init初始化绘制元素,得到为该控件绘制创建的FSlateDrawElement对象。
总结整个Slate绘制流程的第二步,我们没有分析快速处理和优化细节,而是按照正常绘制流程分析代码。通过从PaintWindow开始遍历整个控件树,处理每个空间节点的Paint、OnPaint函数,最终目的是给每个控件创建一个FSlateDrawElement对象,存储渲染线程绘制所需的相关信息,并添加到FSlateWindowElementList中。理解了整个调用流程,整个过程较为清晰,本文基于UE4版本4..2。