QT的Paint 系统

更新时间:2025-12-14 02:04:56 阅读: 评论:0


2022年8月1日发
(作者:汇票样本)

下面对于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()anddrawLines(),,会根据当前设置的brush和pen

绘制给定的QRects数组或者QLines数组。

QPainter还两个了两个函数fillRect用来填充一个QRect,以及eraseRect用来擦除一个

矩形。

上面的绘制命令提供一个整数参数版本,也提供一个浮点参数版本。

如果你需要绘制一个复杂的几何形状,而且需要反复的绘制,建议你使用QPainterPath和

drawPth()。

QPainter还提供了fillPath来填充QPainterPath组成的形状,还有strokePath()来绘制

给定path的边缘(勾边)。

QT也提供了一些列绘图命令来绘制pixmaps和images,它们是:drawPixmap(),

drawImage()anddrawTiledPixmap().其中drawPixmap(),和drawImage()产生的

效果是一样的,只是drawPixmap在屏幕上绘制比较快,而drawImage在QPrinter和其

他设备上绘制会比较快

文字绘制用接口drawText(),绘制文字需要提供坐标,还有boundingRect()

QPainter还有一个接口,用来在painterdevice上绘制一个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或者vectorpath,

当前裁剪区域可以通过clipRegion()和clipPath访问。不同的裁剪区域在不同的

paintEngine()上会获得不一样的性能,在QPainter执行裁剪后,paintedevice还回执行

一次裁剪。

组合模式

QPainter提供了一个枚举CompositionMode类型,用来配置QPainter绘制命令的融合模

式。

两个用得最多的是Source和SourceOver,Source模式用来绘制那些不透明的对象,在

这个模式下,source中的每个像素会代替destination中的相应像素。在SourceOver模式,

主要用来绘制透明对象,在这个模式下,source中的像素不会直接替代destination中的像素,

source中的像素会覆盖在destination上(没明白QTassist中的这个是说什么,但猜测应该

是进行alpha混合,至于alpha的混合是SRCAPHA还是IV_SRCALPHA就不得而知

了,需要实验一下)。

性能

QPainter是一个丰富的绘制框架,为开发者提供了大量的图像绘制命令,比如渐变

(gradients),融合模式(compositionmodes),矢量图像(vectorgraphics)。而且上述的

绘制在大部分软硬件平台都是支持的,为了让大部分的软硬件环境都能运行QT,这种跨平台支

持,自然让我们牺牲了一些性能,可以想象到如果要让QPainter中的每一个单独绘制命令都去

完整的设置一次compositionmodes,brushes,clipping,transformation等等,这种渲

染状态的海量设置几乎是不可能完成的任务。作为一个折中的版本,我们选择了QPainterAPI

中的一个子集和一些后端的关键技术作为突破点,来保证这部分绘制命令的性能达到我们可以接

受程度。

为了实现上述性能目标,我们聚焦的绘制后端技术有如下一些(我们尽力保证他们的性能):

[QTassist中队这一段的描述,E文相对较绕,先贴下原文,可以对照:QPainterisarich

frameworkthatallowsdeveloperstodoagreatvarietyofgraphicaloperations,such

asgradients,intercandothis

llytheunderlying

combinationofhardwareandsoftwarehassomeimplicationsforperformance,and

ensuringthateverysingleoperationisfastincombinationwithallthevarious

combinationsofcompositionmodes,brushes,clipping,transformation,etc,isclose

promisewe

haveselectedasubsetoftheQPainterAPIandbackends,whereperformanceis

guaranteedtobeasgoodaswecansensiblygetitforthegivencombinationof

hardwareandsoftware.]

QT主要实现的后端绘制技术:

Raster(光栅化)-这个后端技术,用纯软件的方法实现渲染,并且他总是会渲染到一

个QImage。为了优化性能,这里的渲染只使用下面的格式:,其他的任何告诉包括,光

栅化的性能都很差。这个渲染引擎也是Windows和QWS上默认的渲染引擎。这个渲

染引擎作为默认的图像系统可以运行在任何操作系统和软硬件平台上,在命令行中通过

-graphicssystemraster就可以指定用这个渲染引擎启动QT。

OpenGL2.0(ES)这个是一个主要的硬件加速的图像后端。这个可以运行在桌面机上,

以及所有支持OpenGL2.0或者OpenGL/ES2.0的设备上。这也就意味着绝大部分

图形芯片都是支持的。这个引擎通过命令行-graphicssystem-opengl启动

QPainter在绘制QGLWidget的时候使用这个图形引擎。

OpenVG-这个后端技术,主要是实现Khronos标准的2D图形和矢量图形。这

个使得QT在支持OpenVG的硬件设备上也是支持的,通过命令行-graphicsssytem

openvg开启。

性能得到保证的主要绘制相关操作包括:

简单的变换,正常的平移和缩放,或者进行0,90,180,270,这种角度的旋转

操作

drawPixmap()结合简单的变换,绘制不透明的对象(这个时候CompositionMode

不要设置成QPainter::SmoothPixmapTransform,这个时候不支持)

矩形纯填充,或者两个颜的渐变填充,或者还加上一些简单的变换都是ok的。

模式设置成QPainter::CompositionMode_Sourceand

QPainter::CompositionMode_SourceOver,性能是最好的。

绘制引擎

用纯或者两个颜的渐变填充圆角矩形也是ok的。

用qDrawBorderPixmap进行3*3的pixmaps修补也是ok的。

Qt-painter

QT的绘制系统,封装得比较严实,这里针对GraphicsView-Scene-Item系统做一个

介绍。

QT的PaintSystem主要是基于QPainter,QPainterDevice和QPaintEngine三个类。

er

用于完成绘制操作。

Device

可以看成是一个2维的画板,包含一些画板的基本信息。直译的话就是绘图设备。

Engine

提供了接口,QPainter使用这些接口往不同类型的device上绘制。QPaintEngine不直

接提供给开发人员使用。打个比方,如果你想使用windows自身的绘制设备绘制UI,那么Qt

就选择默认地匹配windows的QPaintEngine进行界面的绘制;如果你想用OpenGL渲染界

面,则需要使用OpenGL相关的QPaintEngine。Qt自带的QGLWidget可使用OpenGL进

行渲染,其内部便使用了QGLPaintEngine。

3DPaintEngine

如果我希望能用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_PAIT和WM_ERASEBKGD开始

然后这个会由一个绘制事件发送给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的内容显示在窗口上呢,看下下面的代码段:

这个位于:boolQETWidget::translatePaintEvent(constMSG&msg)

也就是

中的相应窗口的WIDOWS的PAIT消息。

有机会再分析QT的光栅渲染引擎。


本文发布于:2022-08-01 16:50:07,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/falv/fa/78/50928.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 站长QQ:55-9-10-26