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

时间: 2024-12-13 09:21:05

QT的Paint 系统的相关文章

[Qt入门篇]5 Qt的属性系统——声明属性

[Qt入门篇]5 Qt的属性系统--声明属性 Qt提供了灵活的属性系统,它基于Qt的元对象系统,不依赖于编译器,这保证了Qt独立于编译其和平台的特点.这篇文章主要看看如何声明属性. 属性系统比较复杂,先看一个简单的例子.在QWidget中,有很多属性的声明,找一个简单学习: Q_PROPERTY(boolmodalREADisModal) 这里出现了5个元素:Q_PROPERTY.bool.modal.READ.isModal.这五个元素都是啥作用呢? Q_PROPERTY:用于声明属性的宏:

QT元对象系统简介

 QT元对象系统   qt的主要成就之一就是使用了一种机制对C++进行了扩展,并且使用这种机制创建了独立的软件组件,这些组件可以绑定在一起,但任何一个组件对于它所要连接的组件的情况一无所知,这种机制就称为元对象系统,它提供了关键的两个技术,信号和槽,以及内省.     内省功能对于实现信号和槽是必须的,并且容许开发人员在运行时获取有关QObject子类的"元信息",包括一个含有对象的类名以及它所支持的信号和槽的列表,这一机制也支持属性和文本翻译,并且为Qtscript模块奠定了基础,标

Qt之资源系统

简述 Qt 的资源系统用于存储应用程序的可执行二进制文件,它采用平台无关的机制.当你的程序总需要这样的一系列文件(图标.翻译文件等)并且不想冒丢失某些文件的风险时,这就显得十分有用. 资源系统基于 qmake.rcc(Qt 资源编译器) 和 QFile 之间的紧密合作. 简述 资源集合文件qrc 外部二进制资源 内编译资源 压缩 在程序中使用资源 在库中使用资源 更多参考 资源集合文件(.qrc) 与程序相关的资源在被指定在一个 .qrc 文件中,其基于 XML 的文件格式列出了磁盘上的文件,可

QT在英文系统下乱码问题

我的QT环境: 源代码的字符集:gb18030 QT的项目文件pro的字符集:CODECFORTR = GB18030 在英文系统上显示乱码 原因:英文系统上如果要显示中文,可以通过加载QTplugins下的插件来进行解码,所以要想在英文系统上正确显示中文就只能是通过unicode编码了, 解决方案: 1.我采用的是utf8编码,我没有修改源代码的字符集,而是将所以由tr()包封的字符串通过uic工具,在designer下将其转换为utf8的字符串. 2.使用翻译的时候,将pro文件的字符集修改

Qt设置windows系统时间

Qt设置windows系统时间网上的回答很多,但很少有一步到位的,一般会有8小时时差,下面给出直接解决代码: #include "windows.h" void Dialog::setTime()  {    SYSTEMTIME st;    GetLocalTime(&st);//关键在这里    st.wHour=ui->spinBox->value();    st.wMinute=ui->spinBox_2->value();    st.wS

什么是Qt元对象系统

Qt元对象系统,即meta object system(mos),提供了大家熟知的用于对象间信息传递的信号与槽机制,运行时类型信息和动态属性系统. mos基于三件法宝: 一是QObject类,是所有Qt对象的基类,可以很好的使用mos. 二是Q_OBJECT宏,在类private部分声明,用于激活mos特性,例如动态属性.信号与槽. 三是元对象编译器,即meta object compiler(moc),为QObject的派生类提供了必要的代码以实现mos特性.例如Example.h中声明了Ex

【Qt编程】系统托盘的显示

本文主要讨论Qt中的系统托盘的设置.系统托盘想必大家都不陌生,最常用的就是QQ.系统托盘以简单.小巧的形式能让人们较快的打开软件.废话不多说,下面开始具体介绍. 首先,新建一个Qt Gui项目,类型选择QMainWindow,类名也选择默认的mainwindow.这时,工程项目中就有3个文件(界面文件不用管):mainwindow.h mainwindow.cpp main.cpp.然后,分别修改3个文件如下: 1.mainwindow.h #ifndef MAINWINDOW_H #defin

Qt元对象系统源码解析

Qt元对象系统源码解析 https://blog.51cto.com/9291927/2070348 一.Qt元对象系统简介 1.元对象系统简介 Qt 的信号槽和属性系统基于在运行时进行内省的能力,所谓内省是指面向对象语言的一种在运行期间查询对象信息的能力, 比如如果语言具有运行期间检查对象型别的能力,那么是型别内省(type intropection)的,型别内省可以用来实施多态.C++的内省比较有限,仅支持型别内省, C++的型别内省是通过运行时类型识别(RTTI)(Run-Time Typ

Qt 元对象系统(Meta-Object System)

(转自:http://blog.csdn.net/aladdina/article/details/5496891) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的对象提供了一个基类. 宏:Q_OBJECT,通常可以声明在类的私有段中,让该类可以使用元对象对象的特性,比如动态属性,信号和槽. 编译器:元对象编译器(moc)为每个QObject子对象自动生成必要的代码来实现元对象特性. moc工具会读入C++的源文件,如果它发现了一个或者多个声明了Q_OBJEC