QPainter 类在widgets和其他绘制设备上执行底层绘制。
QPainter 提供高度优化的功能来完成 GUI 程序所需的大部分绘图。它可以绘制从简单线条到复杂形状(如饼图和和折线)的所有内容。它还可以绘制对齐的文本和像素图。通常,它绘制“自然”坐标系,但它也可以进行视图和世界转换。QPainter 可以对继承 QPaintDevice 类的任何对象进行操作。QPainter 的常见用途是在widgets的绘制事件中:构造和自定义(例如设置笔或画笔)painter。然后绘图。记得在绘制后销毁 QPainter 对象。
void SimpleExampleWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(Qt::blue);
painter.setFont(QFont("Arial", 30));
painter.drawText(rect(), Qt::AlignCenter, "Qt");
}
QPainter 的核心功能是绘图,但该类还提供了几个函数,允许您自定义 QPainter 的设置及其渲染质量,以及其他启用剪切的功能。此外,您还可以通过指定绘制者的合成模式来控制如何将不同的形状合并在一起。
isActive() 函数指示画家是否处于活动状态。Painter 由 begin() 函数和采用 QPaintDevice 参数的构造函数激活。end() 函数和析构函数将其停用。
与QPaintDevice和QPaintEngine类一起,QPainter构成了Qt绘图系统的基础。QPainter 是用于执行绘制操作的类。QPaintDevice 表示可以使用 QPainter 绘制的设备。QPaintEngine 提供了Painter 用来绘制到不同类型的设备上的界面。如果Painter 处于活动状态,则 device() 返回 Painter 在其上绘制的绘画设备,paintEngine() 返回绘制器当前正在运行的paintEngine。
有时,最好让别人在不寻常的QPaint设备上绘画。QPainter 支持一个静态函数来做到这一点,setRedirected()。
警告:当 paintdevice 是一个小部件时,QPainter 只能在 paintEvent() 函数内部或 paintEvent() 调用的函数中使用。
设置(Settings)
可以自定义几种设置,以根据需要进行 QPainter 绘制:
font() 是用于绘制文本的字体。如果绘制器是 Active(),则可以分别使用 fontInfo() 和 fontMetrics() 函数检索有关当前设置的字体及其度量的信息。
brush() 定义用于填充形状的颜色或图案。
pen() 定义用于绘制线条或边界的颜色或点画。
backgroundMode() 定义是否存在 background(),即它是 Qt::OpaqueMode 或 Qt::TransparentMode。
background() 仅在 backgroundMode() 是 Qt::OpaqueMode 且 pen() 是 stipple 时才适用。在这种情况下,它描述了小节中背景像素的颜色。
brushOrigin() 定义了平铺画笔的原点,通常是小部件背景的原点。
viewport(), window(), worldTransform() 构成了Painter 的坐标变换系统。
hasClipping() 告诉画家是否剪辑。(绘画装置也会夹住。如果Painter剪辑,它会剪辑到 clipRegion()。
layoutDirection() 定义绘制者在绘制文本时使用的布局方向。
worldMatrixEnabled() 指示是否启用了 world transformation。
viewTransformEnabled() 指示是否启用了视图转换。
请注意,其中一些设置反映了某些绘画设备中的设置,例如 QWidget::font()。QPainter::begin() 函数(或等效于 QPainter 构造函数)从绘制设备复制这些属性。
您可以随时通过调用 save() 函数来保存 QPainter 的状态,该函数将所有可用设置保存在内部堆栈上。restore() 函数将它们弹出回来。
绘图(Drawing)
QPainter 提供了绘制大多数原语的函数:drawPoint()、drawPoints()、drawLine()、drawRect()、drawRoundedRect()、drawEllipse()、drawArc()、drawPie()、drawChord()、drawPolyline()、drawPolygon()、drawConvexPolygon() 和 drawCubicBezier()。两个方便的函数,drawRects() 和 drawLines(),使用当前的笔和画笔在给定的 QRects 或 QLines 数组中绘制给定数量的矩形或线条。
QPainter 类还提供了填充给定 QRect 的 fillRect() 函数,以及擦除给定矩形内区域的 eraseRect() 函数。所有这些函数都有整数和浮点版本。
如果需要绘制复杂形状,尤其需要重复绘制的时候,可以考虑使用drawPath() 函数创建一个 QPainterPath 进行绘制。
QPainter 还提供了 fillPath() 函数,该函数用给定的 QBrush 填充给定的 QPainterPath,以及绘制给定路径轮廓的 strokePath() 函数(即描划路径)。
文本绘制是使用 drawText() 完成的。当你需要细粒度定位时,boundingRect() 会告诉你给定的 drawText() 命令将绘制在哪里。
绘制图片( Drawing Pixmaps and Images )
Qt提供了绘制pixmap/images的函数,即drawPixmap(),drawImage() 和drawTiledPixmap() 。drawPixmap()和 drawImage() 都会产生相同的结果,除了 drawPixmap() 在屏幕上更快,而 drawImage() 在 QPrinter 或其他设备上可能更快。
有一个drawPicture() 函数可以绘制整个QPicture的内容。drawPicture() 函数是唯一忽略所有QPainter 设置的函数,因为 QPicture 有自己的设置。
绘制高分辨率版本的像素图Pixmaps和图像Images
高分辨率版本的像素映射具有大于 1 的设备像素比率值(请参阅 QImageReader,QPixmap::devicePixelRatio())。如果它与基础 QPaintDevice 的值匹配,则会将其直接绘制到设备上,而不应用其他转换。
例如,将设备像素比为 2 的 64x64 像素大小的 QPixmap 绘制到设备像素比为 2 的高 DPI 屏幕上时,就是这种情况。请注意,像素映射在用户空间中实际上是 32x32 像素。Qt中根据像素图大小计算布局几何图形的代码路径将使用此大小。这样做的净效果是像素图显示为高 DPI 像素图而不是大像素图。
渲染质量
要使用 QPainter 获得最佳渲染效果,应使用独立于平台的 QImage 作为绘画设备;即使用 QImage 将确保结果在任何平台上都具有相同的像素表示。QPainter 类还提供了一种通过其 RenderHint 枚举和对浮点精度的支持来控制渲染质量的方法:所有用于绘制基元的函数都有一个浮点版本。这些通常与 QPainter::Antialiasing 抗锯齿渲染提示结合使用。
RenderHint 枚举指定 QPainter 的标志,任何给定引擎可能会也可能不会遵守这些标志。 QPainter::Antialiasing表示引擎应尽可能抗原语的边缘锯齿, QPainter::TextAntialiasing 表示引擎应尽可能抗锯齿文本,QPainter::SmoothPixmapTransform 表示引擎应使用平滑像素图转换算法。
renderHints() 函数返回一个标志,该标志指定为此绘制者设置的呈现提示。使用 setRenderHint() 函数设置或清除当前设置的 RenderHints。
坐标变换( Coordinate Transformations )
常规状态,QPainter 只在设备本身坐标系统上进行操作(通常是像素),但是QPainter 对坐标变换具有较好的支持。比如 :
最常用的变换是缩放、旋转、平移和扭曲变换。使用 scale() 函数按给定偏移量缩放坐标系,使用 rotate() 函数顺时针旋转坐标系,使用 translate() 平移坐标系(即向点添加给定的偏移量)。您还可以使用 shear() 函数围绕原点扭转坐标系。
所有的变换都在 worldTransform() 上操作。矩阵将平面中的一个点转换为另一个点。setWorldTransform() 函数可以替换或添加到当前设置的 worldTransform() 中。resetTransform() 函数重置使用 translate()、scale()、shear()、rotate()、setWorldTransform()、setViewport() 和 setWindow() 函数进行的任何转换。deviceTransform() 返回从逻辑坐标转换为平台相关绘制设备的设备坐标的矩阵。后一个函数仅在平台依赖句柄上使用平台绘制命令时才需要,并且平台不会自然地进行转换。
使用 QPainter 绘图时,我们使用逻辑坐标指定点,然后将其转换为绘画设备的物理坐标。逻辑坐标到物理坐标的映射由 QPainter 的 combinedTransform() 处理,这是 viewport() 和 window() 以及 worldTransform() 的组合。viewport() 表示指定任意矩形的物理坐标,window() 在逻辑坐标中描述相同的矩形,worldTransform() 与转换矩阵相同。
QPainter 可以将任何绘图操作裁剪到矩形、区域或矢量路径。当前剪辑可以使用函数 clipRegion() 和 clipPath() 使用。路径或区域是首选的(更快)取决于底层的paintEngine()。例如,QImage 绘画引擎首选路径,而 X11 绘画引擎首选区域。设置clip是在painters的逻辑坐标中完成的。在QPainter的剪裁之后,绘画装置也可能被剪裁。例如,大多数小部件会剪掉子构件使用的像素,大多数打印机会剪掉纸张边缘附近的区域。这种额外的剪切不会反映在 clipRegion() 或 hasClipping() 的返回值中。
合成模式( Composition Modes )
QPainter 提供了 CompositionMode 枚举,它定义了数字图像合成的Porter-Duff规则:它描述了一个模型,用于将一个图像(源)中的像素与另一个图像(目标)中的像素组合在一起。两种最常见的组合形式是 Source 和 SourceOver.。 Source 用于将不透明对象绘制到绘画设备上。在此模式下,源中的每个像素都会替换目标中的相应像素。在 SourceOver 合成模式下,源对象是透明的,并绘制在目标之上。
注:合成转换是像素操作的。因此,使用图形基元本身与其边界矩形之间存在差异:边框包含 alpha == 0 的像素(即基元周围的像素)。这些像素将覆盖另一个图像的像素,有效地清除这些像素,而基元仅覆盖其自己的区域。
局限性( Limitations )
如果您将坐标与Qt基于光栅的绘画引擎一起使用,请务必注意,虽然可以使用大于+/- 215的坐标,但不能保证显示在此范围之外的坐标执行的任何绘画;绘图可能会被剪裁。这是由于在实现中使用了 short int。Qt的笔触器生成的轮廓只是处理弯曲形状时的近似值。在大多数情况下,不可能使用另一个贝塞尔曲线段来表示贝塞尔曲线段的轮廓,因此Qt通过使用几条较小的曲线来近似曲线轮廓。出于性能原因,Qt用于这些轮廓的曲线数量是有限制的,因此当使用较大的笔宽或比例时,轮廓误差会增加。要生成误差较小的轮廓,可以使用QPainterPathStroker类,该类具有setCurveThreshold成员函数,允许用户指定容错。另一种解决方法是先将路径转换为多边形,然后再绘制多边形。
表现(Performance)
QPainter是一个丰富的框架,允许开发人员执行各种图形操作,例如渐变,合成模式和矢量图形。QPainter可以在各种不同的硬件和软件堆栈中做到这一点。当然,硬件和软件的底层组合对性能有一定的影响,并且由于排列的数量,确保每个操作都与组合模式、画笔、剪辑、转换等的所有各种组合快速组合,几乎是不可能的任务。作为折衷方案,我们选择了 QPainter API 和后端的一个子集,其中的性能保证与给定的硬件和软件组合一样好。
作为高性能引擎,我们关注的后端是:
-
栅格 - 此后端在纯软件中实现所有渲染,并始终用于渲染到 QImages。为了获得最佳性能,请仅使用格式类型 QImage::Format_ARGB32_Premultiplied、QImage::Format_RGB32 或 QImage::Format_RGB16。任何其他格式,包括 QImage::Format_ARGB32,性能明显较差。此引擎默认用于 QWidget 和 QPixmap。
-
OpenGL 2.0 (ES) - 此后端是硬件加速图形的主要后端。它可以在支持 OpenGL 2.0 或 OpenGL/ES 2.0 规范的台式计算机和嵌入式设备上运行。这包括过去几年生产的大多数图形芯片。该引擎可以通过在QOpenGLWidget 上使用QPainter来启用。
这些操作是:
-
简单的转换,意味着平移和缩放,加上 0、90、180、270 度旋转。
-
drawPixmap() 与简单变换和非平滑变换模式的不透明度相结合(QPainter::SmoothPixmapTransform 未作为渲染提示启用)。
-
矩形填充纯色、双色线性渐变和简单变换。
-
具有简单变换和相交剪辑的矩形裁剪。
-
组合模式 QPainter::CompositionMode_Source 和 QPainter::CompositionMode_SourceOver
-
使用纯色和双色线性渐变填充的圆角矩形填充。
-
3x3 修补的像素图,通过 qDrawBorderPixmap。
此列表指示了在性能至关重要的应用程序中安全使用哪些功能。对于某些设置,其他操作也可能很快,但在广泛使用它们之前,建议在最终运行软件的系统上对其进行基准测试和验证。在某些情况下,可以使用昂贵的操作,例如,当结果缓存在 QPixmap 中时。
实现过程参考 Qt 帮助文档
后续公众号会发布系列教程,更多内容请关注公众号:程序猿学习日记