若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105506028
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)
《Qt开发技术:Qt绘图系统(一)绘图系统介绍》
《Qt开发技术:Qt绘图系统(二)QPainter详解》
红胖子,来也!
Qt的图形绘制系统,本篇主要介绍绘图类QPainter。
QPainter类在窗口和其他绘制设备上执行低级绘制。
QPainter提供高度优化的功能来完成大多数图形用户界面程序所需的工作。它能画出从简单线条到复杂形状如饼图和弦等一切图形。它还可以绘制对齐的文本和像素图。通常,它绘制一个“自然”坐标系,但它也可以进行视图和世界变换。QPainter可以对继承QPaintDevice类的任何对象进行操作。
QPainter的常见用法是在窗口的绘制事件中:构造和自定义(例如设置笔或画笔)绘制器。然后画。记住在绘制后销毁QPainer对象。例如:
QPainter的核心功能是绘图,但是类还提供了几个功能,允许您自定义QPainter的设置及其渲染质量,以及其他启用剪辑的功能。此外,还可以通过指定绘制者的合成模式来控制不同形状合并在一起的方式。
函数QPainter::isActive()的作用是:指示绘制程序是否处于活动状态。painter由begin()函数和接受QPaintDevice参数的构造函数激活,end()函数和析构函数将停用它。
与QPaintDevice和QPaintEngine类一起,QPainter构成了qt绘图系统的基础。QPainter是用于执行绘图操作的类。QPaintDevice表示可以使用QPainter绘制的设备。QPaintEngine提供了一个接口,绘图使用它来绘制不同类型的设备。如果绘图处于活动状态,则device()返回及绘图在其上进行绘图的绘图设备,paintEngine()返回绘图当前正在使用的绘图引擎。有关详细信息,请参见绘图系统。
有时,要求在不通用的设备上作画。QPainer支持一个静态函数来执行此操作,setRedirected()。
警告:当paintdevice是一个窗口时,QPainter只能在paintEvent()函数或paintEvent()调用的函数中使用。
可以根据自己的喜好自定义多个设置以使QPainer绘制:
请注意,这些设置中的一些反映了某些绘制设备中的设置,例如QWidget::Font()。QPainter::begin()函数(或等效的QPainter构造函数)从绘制设备复制这些属性。
可以随时通过调用save()函数来保存QPainer的状态,该函数将所有可用的设置保存在一个内部堆栈上(入栈)。restore()函数的作用是:将它们恢复(出栈)。
QPainter提供了绘制大多数基本体的函数:drawPoint()、drawPoints()、drawLine()、drawRect()、drawRoundedRect()、drawEllipse()、drawArc()、drawPie()、drawChord()、drawPolyline()、drawPolygon()、drawConverExpolygon()和drawCubicBezier()。drawRects()和drawLines()这两个函数使用当前的画笔在给定的QRects或QLines数组中绘制给定数量的矩形或线条。
QPainer类还提供了fillRect()函数,该函数用给定的QBrush填充给定的QRect,以及擦除给定矩形内区域的eraseRect()函数。
所有这些函数都有整数和浮点版本(推荐使用浮点数版本)。
测试对象为QWidget,重载paintEvent函数绘制,如下图:
void Widget::drawText(QPainter *painter)
{
painter->save();
painter->drawText(rect(), Qt::AlignCenter, "Hello Qt!!!");
painter->restore();
}
void Widget::drawPoint(QPainter *painter)
{
painter->save();
painter->drawPoint(10, 10);
QPoint point(20, 20);
painter->drawPoint(point);
QPointF pointF(30, 30);
painter->drawPoint(pointF);
painter->restore();
}
void Widget::drawPoints(QPainter *painter)
{
painter->save();
QPoint point[5];
point[0].setX(10);
point[0].setY(10);
point[1].setX(20);
point[1].setY(20);
point[2].setX(30);
point[2].setY(30);
point[3].setX(40);
point[3].setY(40);
point[4].setX(50);
point[4].setY(50);
painter->drawPoints(point, 5);
QPolygon polypon;
polypon << QPoint(10, 70);
polypon << QPoint(20, 70);
polypon << QPoint(30, 70);
polypon << QPoint(40, 70);
polypon << QPoint(50, 70);
polypon << QPoint(60, 70);
painter->drawPoints(polypon);
painter->restore();
}
void Widget::drawLine(QPainter *painter)
{
painter->save();
QLine line(0, 0, 100, 100);
painter->drawLine(line);
painter->drawLine(0,100, 100, 0);
painter->drawLine(QPoint(50, 0), QPoint(50, 100));
painter->restore();
}
void Widget::drawLines(QPainter *painter)
{
QVector vectorLine;
vectorLine << QLine(10, 0, 10, 100);
vectorLine << QLine(30, 0, 30, 100);
vectorLine << QLine(50, 0, 50, 100);
vectorLine << QLine(70, 0, 70, 100);
vectorLine << QLine(90, 0, 90, 100);
painter->drawLines(vectorLine);
}
void Widget::drawPolyline(QPainter *painter)
{
painter->save();
QPoint point[5];
point[0].setX(10);
point[0].setY(10);
point[1].setX(20);
point[1].setY(10);
point[2].setX(40);
point[2].setY(40);
point[3].setX(40);
point[3].setY(60);
point[4].setX(50);
point[4].setY(50);
painter->drawPolyline(point, 5);
QPolygon polypon;
polypon << QPoint(10, 70);
polypon << QPoint(20, 70);
polypon << QPoint(30, 70);
polypon << QPoint(40, 70);
polypon << QPoint(50, 70);
polypon << QPoint(60, 90);
painter->drawPolyline(polypon);
painter->restore();
}
void Widget::drawPolygon(QPainter *painter)
{
painter->save();
QPoint point[5];
point[0].setX(10);
point[0].setY(10);
point[1].setX(20);
point[1].setY(10);
point[2].setX(40);
point[2].setY(40);
point[3].setX(40);
point[3].setY(60);
point[4].setX(50);
point[4].setY(50);
// Qt::OddEvenFill
// 指定使用奇数-偶数填充规则填充区域。根据此规则,
// 我们使用以下方法确定点是否在形状内。
// 从点到形状外的位置绘制一条水平线,并计算相交的数目。
// 如果交点的数目是奇数,则该点位于形状内部。此模式为默认模式
// Qt::WindingFill
// 指定使用非零缠绕规则填充区域。根据此规则,
// 我们使用以下方法确定点是否在形状内。
// 从点到形状外部的位置绘制一条水平线。确定每个交叉点的直线方向向上还是向下。
// 绕组数是通过求和每个交叉点的方向来确定的。如果数字不是零,则点在形状内。
// 这种填充模式在大多数情况下也可以被视为闭合形状的交点。
painter->drawPolygon(point, 5, Qt::OddEvenFill);
// painter->drawPolygon(point, 5, Qt::WindingFill);
painter->restore();
}
void Widget::drawRect(QPainter *painter)
{
painter->save();
painter->drawRect(10, 10, 40, 40);
QRect rect;
rect.setX(50);
rect.setY(50);
rect.setWidth(10);
rect.setHeight(10);
painter->drawRect(rect);
painter->restore();
}
void Widget::drawRects(QPainter *painter)
{
painter->save();
QRect rect[2];
rect[0].setX(10);
rect[0].setY(10);
rect[0].setWidth(10);
rect[0].setHeight(10);
rect[1].setX(40);
rect[1].setY(40);
rect[1].setWidth(10);
rect[1].setHeight(10);
painter->drawRects(rect, 2);
painter->restore();
}
void Widget::drawRoundRect(QPainter *painter)
{
painter->save();
painter->drawRoundRect(20, 20, 20, 20, 100, 100); // 后两个参数为百分比
painter->drawRoundedRect(50, 50, 20, 20, 10, 5);
painter->restore();
}
void Widget::drawEllipse(QPainter *painter)
{
painter->save();
painter->drawEllipse(0, 0, 100, 100);
painter->drawEllipse(50, 50, 20, 40);
painter->restore();
}
void Widget::drawArc(QPainter *painter)
{
painter->save();
painter->drawArc(0, 0, 100, 100, 0 * 16, 270 * 16);
painter->drawArc(20, 20, 60, 60, 270 * 16, 90 * 16);
painter->restore();
}
void Widget::drawChord(QPainter *painter)
{
painter->save();
painter->drawChord(0, 0, 100, 100, 0 * 16, 270 * 16);
painter->drawChord(20, 20, 60, 60, 270 * 16, 90 * 16);
painter->restore();
}
void Widget::drawPie(QPainter *painter)
{
painter->save();
painter->drawPie(0, 0, 100, 100, 0 * 16, 270 * 16);
painter->drawPie(20, 20, 60, 60, 270 * 16, 90 * 16);
painter->restore();
}
void Widget::drawPath(QPainter *painter)
{
painter->save();
QPainterPath painterPath;
painterPath.addRect(10, 10, 80, 80);
painterPath.addEllipse(QRect(20, 20, 60, 60));
painterPath.addRegion(QRegion(30, 30, 40, 40, QRegion::Ellipse));
painter->drawPath(painterPath);
painter->restore();
}
void Widget::drawPixmap(QPainter *painter)
{
painter->save();
QPixmap pixmap;
pixmap.load("D:/1.bmp");
painter->drawPixmap(0, 0, 20, 20, pixmap);
QImage image;
image.load("D:/1.bmp");
painter->drawImage(30, 30, image);
painter->restore();
}
有绘制pixmap/image的函数,即drawPixma()、drawImage()和drawTiledPixmap()。drawPixmap()和drawImage()都会产生相同的结果,只是drawPixmap()在屏幕上更快,而在QPrinter或其他设备上drawImage()可能更快。
有一个drawPicture()函数可以绘制整个QPicture的内容。drawPicture()函数是唯一一个忽略所有绘制设置的函数,因为QPicture有自己的设置。
高分辨率版本的像素映射的设备像素比值大于1(请参见QImageReader,QPixmap::devicePixelRotio())。如果它与基础QPaintDevice的值匹配,则直接将其绘制到设备上,而不应用其他转换。
例如,在高dpi屏幕上绘制64x64像素大小的QPixmap(设备像素比为2)时,设备像素比也为2。请注意,pixmap在用户空间中有效地为32x32像素。qt中基于pixmap大小计算布局几何图形的代码路径将使用此大小。这样做的效果是,像素地图显示为高dpi像素地图,而不是大像素地图。
要使用QPainter获得最佳渲染结果,应使用平台独立的QImage作为绘制设备;即使用QImage将确保结果在任何平台上具有相同的像素表示。
QPainter类还通过其RenderHint枚举和对浮点精度的支持提供了一种控制渲染质量的方法:用于绘制原语的所有函数都具有浮点版本。这些通常与QPainer::Antialiasing渲染结合使用。
抗锯齿是QPainter的渲染默认参数之一。QPainter::RenderHints用于向QPainter指定任何给定引擎可能遵守或不遵守的标志。QPainter::Antialising表示引擎应该尽可能消除原语边缘的混叠,即在原语边缘周围放置额外的像素以平滑边缘。
浮点精度和整数精度之间的差异是一个精度问题,并且在应用程序的主窗口中可见:即使计算圆的几何图形的逻辑是相同的,但浮点确保每个圆之间的空白大小相同,而整数使两个和两个圆成为假装他们属于一起。原因是基于整数的精度依赖于非整数计算的舍入。
RenderHint枚举指定任何给定引擎可能遵守或不遵守的QPainer标志。
renderHints() 函数的作用是:返回一个标志,用于指定为此绘制程序设置的渲染参数美剧。使用setRenderHint()函数设置或清除当前设置的RenderHints。
通常,QPainter在设备自己的坐标系统(通常是像素)上工作,但QPainter对坐标转换有很好的支持。
最常用的转换是缩放、旋转、平移和剪切。使用scale()函数按给定的偏移量缩放坐标系,使用rotate()函数顺时针旋转坐标系,使用translate()将坐标系平移(即向点添加给定的偏移量)。还可以使用shear()函数围绕原点扭曲坐标系。有关剪切坐标系的可视化,请参见仿射变换示例。
所有变换操作都在转换worldTransform()上操作。矩阵将平面中的一个点转换为另一个点。有关转换矩阵的详细信息,请参见坐标系和QTransform文档。
setWorldTransform()函数的作用是:替换或添加到当前设置的worldTransform()。resetTransform()函数的作用是:重置使用translate()、scale()、shear()、rotate()、setworldTransform()、setviewport()和setwindow()函数进行的任何转换。deviceTransform()返回从逻辑坐标转换为平台相关绘制设备的设备坐标的矩阵。只有在依赖于平台的句柄上使用平台绘制命令时才需要后一个函数,并且平台不会自然地进行转换。
使用QPainer绘制时,我们使用逻辑坐标指定点,然后将逻辑坐标转换为绘制设备的物理坐标。逻辑坐标到物理坐标的映射由QPainer的combinedTransform()、viewport()和window()以及worldTransform()的组合处理。viewport()表示指定任意矩形的物理坐标,window()以逻辑坐标描述相同的矩形,worldTransform()与转换矩阵相同。
剪切的作用是通过对元素进行剪切来控制元素的可显示区域,也就是在 clip 指定的范围内的内容才能显示出来,正是使用任意形状的 QPainterPath 作为 QPainter的clip,就能绘制出我们想要的效果,例如绘制上图显示的圆形和星形的图片。
QPainer可以将任何绘图操作剪切到矩形、区域或矢量路径。当前剪辑可以使用函数clipRegion()和clipPath())。路径或区域是否首选(更快),取决于基础paintEngine()。例如,QImage绘制引擎偏好路径,而x11绘制引擎偏好区域。设置剪辑是在画图的逻辑坐标中完成的。
QPainter剪切后,绘制设备也可以被剪切。例如,大多数窗口会剪切掉子窗口使用的像素,而大多数打印机会剪切掉靠近纸张边缘的区域。clipRegion()或hasClipping()的返回值不反映此附加剪切。
QPainer提供了QPainter::CompositionMode枚举,它定义了数字图像合成的Porter-Duff规则;它描述了一个模型,用于将一个图像中的像素、源、另一个图像中的像素、目标组合在一起。
两种最常见的组合形式是Source和SourceOver。Source用于将不透明对象绘制到绘制设备上。在此模式下,源中的每个像素都将替换目标中的相应像素。在SourceOver合成模式中,源对象是透明的,并绘制在目标的顶部。
请注意,组合转换操作pixelwise。因此,使用图形基元本身和其边界矩形之间存在差异:边界矩形包含alpha==0的像素(即基元周围的像素)。这些像素将覆盖另一个图像的像素,有效地清除这些像素,而Source只覆盖其自身的区域。
QPainter是一个丰富的框架,它允许开发人员进行各种各样的图形操作,如渐变、合成模式和矢量图形。而QPainer可以跨各种不同的硬件和软件堆栈执行此操作。当然,硬件和软件的底层组合对性能有一定的影响,并且由于排列的数量多,确保每个操作与所有组合模式、画笔、剪切、转换等的各种组合结合起来都很快就接近一个不可能完成的任务。作为一个折衷方案,我们选择了QPainer API和后端的一个子集,在这里,性能可以保证在给定的硬件和软件组合下尽可能好地得到它。
作为高性能引擎,我们关注的后端是:
这些操作包括:
此列表指示在性能至关重要的应用程序中安全使用哪些功能。对于某些设置,其他操作也可能很快,但在广泛使用它们之前,建议在最终运行软件的系统上对它们进行基准测试和验证。也有一些情况下可以使用昂贵的操作,例如当结果缓存在QPixmap中时。
定义数字图像合成支持的模式。合成模式用于指定一个图像(源)中的像素如何与另一个图像(目标)中的像素合并。
请注意,位光栅操作模式(用RasterOp前缀表示)仅在x11和光栅绘制引擎中支持。这意味着在Mac上使用这些模式的唯一方法是通过QImage。笔和画笔(带alpha组件的)不支持RasterOp表示的混合模式。此外,打开QPainer::Antialiasing渲染提示将有效地禁用RasterOp模式。
最常见的类型是SourceOver(通常称为alpha混合,可理解为像素颜色通道),其中源像素在目标像素的顶部混合,这样源的alpha组件定义像素的半透明性。
一些合成模式需要源图像或目标图像中的alpha通道才能产生效果。为了获得最佳性能,最好使用图像格式“format_ARGB8565_Premultiplied”(A-透明,R-红,G-绿,B-蓝,8565依次代表8位、5位、6位、5位)。
当设置合成模式时,它适用于所有绘制操作器、pens、brushes、gradients和pixmap/image。
枚举常量 |
值 |
描述 |
QPainter::CompositionMode_SourceOver |
0 |
这是默认模式。源的alpha用于覆盖目标上的像素 |
QPainter::CompositionMode_DestinationOver |
1 |
目标的alpha用于在源像素上覆盖源的。此模式与CompositionMode_SourceOver相反。 |
QPainter::CompositionMode_Clear |
2 |
目标中的像素被清除(设置为完全透明),与源无关。 |
QPainter::CompositionMode_Source |
3 |
输出是源像素。(这意味着基本的复制操作,并且在源像素不透明时与SourceOver相同)。 |
QPainter::CompositionMode_Destination |
4 |
输出是目标像素。这意味着混合没有效果。此模式与CompositionMode_Source相反。 |
QPainter::CompositionMode_SourceIn |
5 |
输出是源,其中alpha被目标的alpha减小。 |
QPainter::CompositionMode_DestinationIn |
6 |
输出是目标,其中alpha被源的alpha减小。此模式与CompositionMode_SourceIn相反。 |
QPainter::CompositionMode_SourceOut |
7 |
输出是源,其中alpha被目标的倒数减少。 |
QPainter::CompositionMode_DestinationOut |
8 |
输出是目的地,其中alpha被源的倒数减去。此模式与CompositionMode_SourceOut相反。 |
QPainter::CompositionMode_SourceAtop |
9 |
源像素混合在目标像素上。 |
QPainter::CompositionMode_DestinationAtop |
10 |
目标像素混合在源的顶部,目标像素的阿尔法被目标像素的阿尔法降低。此模式与CompositionMode_SourceAtop相反。 |
QPainter::CompositionMode_Xor |
11 |
源的alpha与目标alpha的倒数相减,而目标的alpha与源alpha的倒数相减。CompositionMode_Xor与位异或不同。 |
QPainter::CompositionMode_Plus |
12 |
源像素和目标像素的alpha和颜色都被添加到一起。 |
QPainter::CompositionMode_Multiply |
13 |
输出是源颜色乘以目标颜色。将颜色与白色相乘将保持颜色不变,而将颜色与黑色相乘将生成黑色。 |
QPainter::CompositionMode_Screen |
14 |
源颜色和目标颜色颠倒,然后相乘。用白色屏蔽颜色会产生白色,而用黑色屏蔽颜色则不会改变颜色。 |
QPainter::CompositionMode_Overlay |
15 |
根据目标颜色倍增或筛选颜色。目的地颜色与源颜色混合,以反映目的地的明暗程度。 |
QPainter::CompositionMode_Darken |
16 |
选择源颜色和目标颜色中较深的颜色。 |
QPainter::CompositionMode_Lighten |
17 |
将选择源颜色和目标颜色的浅色。 |
QPainter::CompositionMode_ColorDodge |
18 |
目标颜色变亮以反映源颜色。黑色源颜色使目标颜色保持不变。 |
QPainter::CompositionMode_ColorBurn |
19 |
目标颜色变暗以反映源颜色。白色源颜色保持目标颜色不变。 |
QPainter::CompositionMode_HardLight |
20 |
根据源颜色倍增或筛选颜色。光源颜色将使目标颜色变亮,而暗光源颜色将使目标颜色变暗。 |
QPainter::CompositionMode_SoftLight |
21 |
根据源颜色变暗或变亮颜色。类似于CompositionMode_HardLight。 |
QPainter::CompositionMode_Difference |
22 |
从较浅的颜色中减去较深的颜色。使用白色绘制将反转目标颜色,而使用黑色绘制将保持目标颜色不变。 |
QPainter::CompositionMode_Exclusion |
23 |
类似于合成模式的差异,但对比度较低。使用白色绘制将反转目标颜色,而使用黑色绘制将保持目标颜色不变。 |
QPainter::RasterOp_SourceOrDestination |
24 |
对源像素和目标像素(src OR dst)执行按位或运算。 |
QPainter::RasterOp_SourceAndDestination |
25 |
对源像素和目标像素(src AND dst)执行按位与运算。 |
QPainter::RasterOp_SourceXorDestination |
26 |
对源像素和目标像素(src XOR dst)执行逐位XOR操作。 |
QPainter::RasterOp_NotSourceAndNotDestination |
27 |
对源像素和目标像素((NOT src) AND (NOT dst))执行按位“或”运算。 |
QPainter::RasterOp_NotSourceOrNotDestination |
28 |
对源像素和目标像素((NOT src) OR (NOT dst))执行逐位NAND操作。 |
QPainter::RasterOp_NotSourceXorDestination |
29 |
执行一个逐位操作,其中源像素被反转,然后与目标((NOT src) XOR dst)进行异或运算。 |
QPainter::RasterOp_NotSource |
30 |
在源像素反转的位置执行逐位操作(NOT src)。 |
QPainter::RasterOp_NotSourceAndDestination |
31 |
执行一个逐位操作,其中源是反向的,然后与目标((NOT src) AND dst)一起使用。 |
QPainter::RasterOp_SourceAndNotDestination |
32 |
执行一个逐位操作,其中源和“ed”使用反转的目标像素(src AND (NOT dst) |
QPainter::RasterOp_NotSourceOrDestination |
33 |
执行一个逐位操作,其中源是反向的,然后与目标((NOT src) OR dst)一起执行或执行。 |
QPainter::RasterOp_SourceOrNotDestination |
34 |
执行一个位操作,其中源是或'与倒置的目标像素(src OR (NOT dst))。 |
QPainter::RasterOp_ClearDestination |
35 |
目标中的像素被清除(set to 0),与源无关。 |
QPainter::RasterOp_SetDestination |
36 |
目标中的像素设置(set to 1)与源无关。 |
QPainter::RasterOp_NotDestination |
37 |
执行目标像素反转(NOT dst)的逐位操作。 |
枚举常量 |
值 |
描述 |
QPainter::OpaqueHint |
0x01 |
指示要绘制的pixmap片段是不透明的。不透明碎片的绘制速度可能更快。 |
这个枚举是在Qt4.7中引入或修改的。
PixmapFragmentHints类型是QFlags
RenderInts用于向QPainer指定任何给定引擎可能遵守或不遵守的标志。
枚举常量 |
值 |
描述 |
QPainter::OpaqueHint |
0x01 |
指示要绘制的pixmap片段是不透明的。不透明碎片的绘制速度可能更快。 |
QPainter::TextAntialiasing |
0x02 |
指示引擎应尽可能消除文本的锯齿。若要强制禁用文本的抗锯齿,请不要使用此提示。相反,在字体的样式策略上设置QFont::NoAntialias。 |
QPainter::SmoothPixmapTransform |
0x04 |
指示引擎应使用平滑的pixmap转换算法(如双线性)而不是最近邻。 |
QPainter::HighQualityAntialiasing |
0x08 |
此值已过时并将被忽略,请改用反锯齿渲染提示。 |
QPainter::NonCosmeticDefaultPen |
0x10 |
此值已过时,qpen的默认值现在为非外观。 |
QPainter::Qt4CompatiblePainting |
0x20 |
兼容性提示,告诉引擎使用与qt 4中相同的基于x11的填充规则,其中混叠渲染的偏移量略小于半个像素。也会将默认构造的钢笔视为外观笔。将Qt4应用程序移植到Qt5时可能有用。 |
RenderInts类型是QFlags
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105506028