QT的Paint 系统
下面对于QT的绘制系统做一个简要说明, 这个系统主要由三部分组成, QPainter, QPaintDevice, QPaintEngine。
QPainter 是一个绘制接口类,提供绘制各种面向用户的命令,而QPaintDevice 是一个QPainter绘制的目的地,相当于画布, 而QPaintEngine 是基本绘制命令的具体实现。
我们打交道比较多的是 QPainter , 注意对于Windows平台来说,当绘制目标是一个widget的时候,QPainter只能在 paintEvent() 里面或者由paintEvent()导致调用的函数里面使用。
QPainter 可以定制如下的一些参数:
font() 字体,辅助接口 fontInfo() 和 fontMetrics()
brush() 定义用填充模式绘制几何形状时候的画刷,主要是画刷的颜色和模式
pen() 定义花框图的时候线条的样条和颜色
backgroundMode() 定义是否存在 background(), 分为, Qt::OpaqueMode 和 Qt::TransparentMode 两个
background() 只有当 backgroundMode() 是 Qt::OpaqueMode, pen() 是 一个 一个stripple (各种虚线。。。。), 这个描述的是背景像素的颜色值。
brushOrigin() 画刷原点,正常情况下,画刷原点就是widget背景的原点
viewport, window() 和 worldTransform(), 一起构成painter的坐标系。
hasClipping() 告诉 painter 是否执行裁剪操作,裁剪的区域是 clipRegion()。
layoutDirection() , 表明的是在绘制文字项的时候,文字的排版方向
worldMatrixEnabled() 告诉绘制流程是否开启 world 变换
viewTransformEnabled() 告诉绘制流程是否开启 view 变换
上面的设置项,很多在绘制的 device 上也会由相应的设置,比如 QWdiget::font()。接口QPainter::begin() 或者是QPainter的构造函数,会从当前的device上拷贝那些属性。
对于QPainter来说,内部有一个状态堆栈,任何时候都可以通过调用 save() 和 restore() 对QPainter的内部状态执行 进栈保存和压栈还原的操作。
QPainter 提供了大部分基本二维几何元的绘制命令,如: drawPoint(), drawPoints(), drawLine(), drawRect(), drawRoundedRect(), drawEllipse(), drawArc(), drawPie(), drawChord(), drawPolyline(), drawPolygon(), drawConvexPolygon() and drawCubicBezier().
其中有两个遍历的函数 drawRects() and drawLines(), ,会根据当前设置的brush和pen绘制给定的QRects数组或者QLines数组。
QPainter 还两个了两个函数 fillRect 用来填充一个 QRect, 以及 eraseRect 用来擦除一个矩形。
上面的绘制命令提供一个整数参数版本,也提供一个浮点参数版本。
如果你需要绘制一个复杂的几何形状,而且需要反复的绘制,建议你使用QPainterPath 和 drawPth()。
QPainter 还提供了 fillPath 来填充 QPainterPath 组成的形状,还有strokePath()来绘制给定 path的边缘(勾边)。
QT也提供了一些列绘图命令来绘制 pixmaps 和 images, 它们是: drawPixmap(), drawImage() and drawTiledPixmap(). 其中 drawPixmap(),和 drawImage() 产生的效果是一样的,只是 drawPixmap在 屏幕上绘制比较快,而drawImage 在 QPrinter 和其他设备上绘制会比较快
文字绘制用接口 drawText(), 绘制文字需要提供坐标,还有boundingRect()
QPainter还有一个接口,用来在 painter device 上绘制一个 QPicture, 接口为 drawPicture(), 这个接口绘制的时候不会使用当前device的状态设置,因为QPicture有它自己的设置。
坐标变换
默认情况下,QPainter 使用的是 当前 device 的坐标系(以像素为单位),但是QPainter 对于坐标系变换提供了很好的支持,主要有如下的一些坐标系变换:
旋转, 缩放, 平移, shearing, 用 scale() 来缩放作响, rotate() 用来对坐标系进行顺时针旋转, translate() 对坐标系执行平移操作。也可以通过函数 shear() 对坐标系执行扭曲。类似对矩阵执行雅克比切变,让 坐标系的 x 和 y 不再是正交的向量。这里提到的变换都是作用在 worldTransform()的矩阵上。 还有一个矩阵 deviceTransform 用来把逻辑坐标变换到设备的坐标。
当用QPainter执行绘制的时候,我们指定的顶点坐标都是逻辑坐标,这个逻辑坐标最终会被转换成设备的物理坐标,从逻辑坐标到物理坐标的转换,是通过矩阵 combinedTransform()执行的,这个矩阵,结合了 viewport() , window(), 和 worldTransform()。 其中 viewport()代表的是物理坐标系中的任意的一个矩形区域,而window()是以逻辑坐标的形式描述viewport()指定的同一个矩形。其中worldTransform() 就等于变换矩阵。
裁剪
QPainter可以把绘制进行裁剪指定,裁剪区域可以是 rectange, region 或者 vector path, 当前裁剪区域可以通过clipRegion() 和 clipPath 访问。不同的裁剪区域在不同的paintEngine()上会获得不一样的性能,在QPainter 执行裁剪后, painte device 还回执行一次裁剪。
组合模式
QPainter 提供了一个枚举 CompositionMode 类型,用来配置QPainter绘制命令的融合模式。
两个用得最多的是 Source 和 SourceOver, Source 模式用来绘制那些不透明的对象,在这个模式下,source中的每个像素会代替destination中的相应像素。在SourceOver模式,主要用来绘制透明对象,在这个模式下,source中的像素不会直接替代destination中的像素,source中的像素会覆盖在destination上(没明白QT assist 中的这个是说什么,但猜测应该是进行 alpha 混合,至于 alpha 的混合是 SRCAPHA 还是 INV_SRCALPHA就不得而知了,需要实验一下)。
性能
QPainter 是一个丰富的绘制框架,为开发者提供了大量的图像绘制命令,比如 渐变(gradients),融合模式(composition modes), 矢量图像(vector graphics)。而且上述的绘制在大部分软硬件平台都是支持的,为了让大部分的软硬件环境都能运行QT,这种跨平台支持,自然让我们牺牲了一些性能,可以想象到如果要让QPainter中的每一个单独绘制命令都去完整的设置一次 composition modes, brushes, clipping, transformation 等等,这种渲染状态的海量设置几乎是不可能完成的任务。作为一个折中的版本,我们选择了QPainter API中的一个子集和一些后端的关键技术作为突破点,来保证这部分绘制命令的性能达到我们可以接受程度。
为了实现上述性能目标,我们聚焦的绘制后端技术有如下一些(我们尽力保证他们的性能):
[QT assist 中队这一段的描述,E文相对较绕,先贴下原文,可以对照:QPainter is a rich framework that allows developers to do a great variety of graphical operations, such as gradients, composition modes and vector graphics. And QPainter can do this across a variety of different hardware and software stacks. Naturally the underlying combination of hardware and software has some implications for performance, and ensuring that every single operation is fast in combination with all the various combinations of composition modes, brushes, clipping, transformation, etc, is close to an impossible task because of the number of permutations. As a compromise we have selected a subset of the QPainter API and backends, where performance is guaranteed to be as good as we can sensibly get it for the given combination of hardware and software.]
QT 主要实现的后端绘制技术:
- Raster(光栅化) - 这个后端技术,用纯软件的方法实现渲染,并且他总是会渲染到一个QImage。为了优化性能,这里的渲染只使用下面的格式:,其他的任何告诉包括,光栅化的性能都很差。这个渲染引擎也是Windows和QWS上默认的渲染引擎。这个渲染引擎作为默认的图像系统可以运行在任何操作系统和软硬件平台上,在命令行中通过 -graphicssystem raster就可以指定用这个渲染引擎启动QT。
- OpenGL 2.0(ES) 这个是一个主要的硬件加速的图像后端。这个可以运行在桌面机上,以及所有支持OpenGL 2.0 或者 OpenGL/ES 2.0的设备上。这也就意味着绝大部分图形芯片都是支持的。这个引擎通过命令行 -graphicssystem -opengl 启动 QPainter 在绘制 QGLWidget 的时候 使用 这个图形引擎。
- OpenVG - 这个后端技术,主要是实现 Khronos 标准的 2D 图形和 矢量图形。 这个使得QT在支持 OpenVG的硬件设备上也是支持的,通过命令行 -graphicsssytem openvg开启。
性能得到保证的主要绘制相关操作包括:
- 简单的变换,正常的平移和缩放,或者进行 0, 90, 180, 270,这种角度的旋转操作
- drawPixmap() 结合简单的变换,绘制不透明的对象(这个时候CompositionMode 不要设置成QPainter::SmoothPixmapTransform, 这个时候不支持)
- 矩形纯色填充,或者两个颜色的渐变填充,或者还加上一些简单的变换都是ok的。
- 模式设置成 QPainter::CompositionMode_Source and QPainter::CompositionMode_SourceOver, 性能是最好的。
- 用纯色或者两个颜色的渐变填充 圆角矩形 也是 ok的。
- 用 qDrawBorderPixmap 进行 3*3的 pixmaps 修补 也是 ok的。
绘制引擎
Qt - painter
QT 的 绘制系统, 封装得比较严实,这里针对GraphicsView - Scene - Item 系统做一个介绍。
QT 的 Paint System 主要是基于 QPainter, QPainterDevice 和 QPaintEngine 三个类。
1.QPainter
用于完成绘制操作。
2.QPaintDevice
可以看成是一个2维的画板,包含一些画板的基本信息。直译的话就是绘图设备。
3.QPaintEngine
提供了接口,QPainter 使用这些接口往不同类型的 device 上绘制。QPaintEngine不直接提供给开发人员使用。打个比方,如果你想使用windows自身的绘制设备绘制UI,那么Qt就选择默认地匹配windows的QPaintEngine进行界面的绘制;如果你想用OpenGL渲染界面,则需要使用OpenGL相关的QPaintEngine。Qt自带的QGLWidget可使用OpenGL进行渲染,其内部便使用了QGLPaintEngine。
4.QD3DPaintEngine
如果我希望能用Windows下的DirectX9 图形API渲染Qt界面的话,我需要创建D3D相关的QPaintEngine。具体实现可以参照QGLWidget。Qt因为跨平台选择了支持OpenGL,对D3D就没提供内部支持了。
在各个平台上,绘制和渲染,通常有三个途径可走,
第一个,用操作系统提供的 api, 操作系统本身通常不会提供三维渲染,只会在基础显示设备的驱动之上封装绘制操作,
第二个,用设备驱动上的渲染库, DirectX, OpenGL, OpeXL
第三个,直接显示设备的操作硬件驱动,比如 皮克斯公司的 renderware ,这个相当于自己做了图形库
从前面知道原生的QT,是一个纯软件实现的光栅化渲染引擎,所以也就没有D3D和OpenGL一说了。不过QT又实现了一套QGLWidget,用OpenGL2.0进行渲染。
绘制流程
深入QT的 Painter 可以看到 ,QT的绘制引擎 种类很多,总共有11类绘制引擎。引擎QT界面库是一个相对底层的库。
我们先看下windows下的这个绘制流程
绘制命令从 Windows 的窗口消息 ,WM_PAINT 和 WM_ERASEBKGND 开始
然后这个会由一个绘制事件发送给 QCoreApplication , 然后又转到了 GraphicsView 的 viewportEvent, 这个中间的转换需要关注一下,是通过filter 转到View的。
这个直接的传递是通过把 相应的 View 当做一个 事件过滤器安装到 Application 上
接着由下面的接口处理绘制事件
我们知道 View 只是一个窗口,View本身没有内容,有内容的是 Scene , 一个Scene 根据层级树的方式挂载这 QGraphicsItem,所以绘制事件就会
传递给 QGraphicsScene ,用来绘制当前 Scene 中的所有 Item。
我们知道 scene 中的 item 是一个具备父子关系的树,所以绘制 scene的时候,我们只需要找到 所有子树的根节点,也就是QT中的toplevelItem。
然后在每个树根上执行递归绘制。
这个过程是一个递归的过程, 来看下 在一个递归循环里面的绘制函数
这个函数 执行一个完整的 绘制流程:
开始 绘制 item 的那些位于 item 之后的 子 item
然后 绘制 item 自己
最后绘制那些 位于 item 之前的 子item
这样一个完整的item绘制流程就完成了。
接下来我们深入 item 本身内容的绘制中, 也就是
这个是一个虚接口函数,由QGraphicsItem的 各个子类实现,这里的绘制是一个高层绘制,也就说在使用 QPainter提供的基本绘制命令上组合图形,
基本绘制命令,有Rect, Pixmap, Point, Lines 等
对于这些组合理论不深入了, 这里接着深入 QPainter 本身。
QPainter 是一个 绘制接口的 Wrapper, 具体的工作都交给 相应的 QPainterEngine
而QPainterEngine 在整个继承层次上,进行分工,分为 PathEngine, RasterEngine, 分别处理线条的绘制,以及 pixelData 的绘制。
前面提到QT支持跨平台,并且支持GL渲染,但这一部分是一个相对独立的系统,和QT的原生系统不一样。
QT原生系统是一个自己做的软件渲染库,基本上事情都在cpu上执行计算,而且整个渲染系统架构在一个自己写的渲染管线上,
QPainter提供的绘制命令只会把所有数据收集整理,然后用自己写的渲染管线执行,裁剪,光栅操作,最后把整理好的渲染位图,填充到一个光栅化的缓冲区,
注意这个缓冲区是内存,不是显存。所以QT是一个软件加速的渲染引擎。至于后面提到的D3D扩展那一套,就当别论了。
下面深入QPainter的 绘制领命, 绘制框图,绘制位图,绘制字体,三个方面。然后再深入介绍一下,QT怎么把它自己写的渲染管线的结果,也就最终渲染buffer,提交给设备。
这其中涉及一个重要的渲染类: QRasterBuffer,这个是QT绘制命令执行的目的地,
而下面的这个结构体是QT用来操作 QRasterBuffer中数据的辅助结构体
这里包括了各种位图操作。
前面我们知道了QT的渲染是软件光栅化,把所有内容都会绘入一个QImage,但它和windows是怎么交互的呢,怎么把QImage的内容显示在窗口上呢,看下下面的代码段:
这个位于:bool QETWidget::translatePaintEvent(const MSG &msg)
也就是
中的相应窗口的 WINDOWS 的PAINT消息。
有机会再分析 QT的 光栅渲染引擎。
http://www.cnblogs.com/JefferyZhou/archive/2012/09/24/2700347.html
QT的Paint 系统的更多相关文章
- Qt之资源系统
简述 Qt 的资源系统用于存储应用程序的可执行二进制文件,它采用平台无关的机制.当你的程序总需要这样的一系列文件(图标.翻译文件等)并且不想冒丢失某些文件的风险时,这就显得十分有用. 资源系统基于 q ...
- QT中自定义系统托盘的实现—c++语言为例
将要介绍的是:QT中自定义系统托盘(systemtray)的一个Demo,希望能帮需要的读者快速上手. 前提假设是诸位已经知道QT中的signals .slot以及资源文件,所以关于这些不会再累述. ...
- 解析Qt元对象系统(五) Q_INVOKABLE与invokeMethod(automatic connection从Qt4.8开始的解释已经与之前不同,发送对象驻足于哪一个线程并不重要,起到决定作用的是接收者对象所驻足的线程以及发射信号(该信号与接受者连接)的线程是不是在同一个线程)good
概述查看Qt源码可知,Q_INVOKABLE是个空宏,目的在于让moc识别. 使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起. Q_INVOKABLE与QMe ...
- 解析Qt元对象系统(四) 属性系统(确实比较方便)
官方解释 我们在Qt源码中可以看到一个QObject的子类经常会用到一些Q_开头的宏,例如QMainWindow类开始部分代码是这样的: Q_PROPERTY(QSize iconSize READ ...
- Qt 元对象系统(Meta-Object System)(不管是否使用信号槽,都推荐使用)
Qt 元对象系统(Meta-Object System) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的对象提供了一个基类. 宏:Q_OBJECT,通常可以声明在类 ...
- qt 使用非系统字库
之前的做法都是把 ttc, ttf 这些文件拷贝到系统字库里去(即拷贝到 lib/fonts 下).但是,每次添加字体,我都要把产品的文件系统都给升级一遍吗?这样系统的一致性就不大好了.所以想能不能直 ...
- Qt之属性系统
简述 Qt提供一个类似于其它编译器供应商提供的复杂属性系统(Property System).然而,作为一个编译器和平台无关的库,Qt不能够依赖于那些非标准的编译器特性,比如:__property或者 ...
- Qt之Meta-Object系统
简述 Qt的元对象系统(Meta-Object System)提供了信号与槽机制,可用于对象间通信.运行时类别信息和动态属性系统. 元对象系统基于三个方面: QObject类:为objects提供了一 ...
- Qt元对象系统简介
在Qt中提供了c++的扩展,提供了一种元对象系统的机制,(meta-object-system)的机制.其中包含了信号与槽的内部机制,能够访问到QObject子类的元对象信息的功能. Q_OBJECT ...
随机推荐
- Android开发之如何保证Service不被杀掉(前台服务)
序言 最近项目要实现这样一个效果:运行后,要有一个service始终保持在后台运行,不管用户作出什么操作,都要保证service不被kill.参考了现今各种定制版的系统和安全厂商牛虻软件,如何能保证自 ...
- ViewPager的简单例子
这个例子是按照官网上的例子写的,有点抄袭的嫌疑,但是自己单独写一下会加深自己的印象. 首先是MainAcitivity.xml: <LinearLayout xmlns:android=&quo ...
- jQuery 日历控件 FullCalendar 初识
最近有个日程管理的需求,就学习了一下 FullCalendar 控件的一些基本知识,本文不是详细介绍该控件的 API 的文档,而是记录本人使用过程中的一些学习情况. 先看一下效果图 月/周/日视图 ...
- C++与Lua交互(三)
通过上一篇的热身,我们对C++调用lua变量有了一个认识,现在让我们再深入一点,去探索一下如何调用lua的函数.表. Lua与宿主通讯的关键--栈 lua是个动态脚本语言,它的数据类型如何映射到C++ ...
- DTCMS自定义标签,tags分割
DTcms.Web.UI\Label\article.cs /// <summary> /// 自定义:分割tags /// </summary> /// <param ...
- hadoop中遇到的问题。
1.物理主机中无法访问管理界面,在虚拟主机中可以访问, 这跟防火墙有关系,重启一下防火墙,然后关闭,最后重启一下handoop,应该就可以了!!!!(hadoop首战顺利!!!!!(●'◡'●))
- SIM900A访问HTTP的简单方法
最近做项目,使用Arduino控制设备,读取数据,然后通过移动网络传送到服务器. 我选用的是正点原子的SIM900A模块.在服务器部署了一个监听程序,Arduino控制SIM900A通过TCP方式把数 ...
- JS禁用浏览器退格键
我们在真实的项目开发中经常会使用JS 对键盘上的一些按键进行禁用,常见的比如说退格键(backspace/ 后退键),我在一个项目中就遇到过在页面编辑的时候禁用掉退格键,因为退格键会发生页面后退,代码 ...
- dive into python 读笔(2)
chapter 4 自省, summary: # 用可选和命名参数定义和调用函数 # 用 str 强制转换任意值为字符串形式 # 用 getattr 动态得到函数和其它属性的引用 # 扩展列表解析语法 ...
- mysql 连接丢失错误解决(转载)
1.1 错误信息: Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet succes ...