4、图形绘制引擎:QPainter基础、绘制基本图元(点、线、矩形、圆)、画笔与画刷设置、抗锯齿与渲染优化
好,咱们今天聊聊EPS软件里最核心、也最直观的部分——图形绘制引擎。说白了,就是怎么在屏幕上画出那些点、线、面。你想想看,一个绘图软件,如果连基本图形都画不漂亮,那用户肯定扭头就走。我当年刚入行时,就吃过这个亏,画出来的线总是有毛刺,被老大骂了一顿。从那以后,我就把QPainter摸得透透的。
4.1 QPainter:你的绘图画笔
QPainter是Qt里负责绘制的核心类。你可以把它想象成一支真正的画笔。它本身不存储图形,它只负责“画”这个动作。你需要给它一个“画布”,也就是一个QPaintDevice,比如QWidget、QImage或者QPixmap。
我个人习惯,在paintEvent事件里创建QPainter对象。为什么?因为这是Qt推荐的官方做法,能保证绘制操作在正确的上下文中执行。代码大概长这样:
void MyWidget::paintEvent(QPaintEvent *) {
QPainter painter(this);
// 开始画吧!
painter.drawLine(10, 10, 100, 100);
}
嗯,这里要注意:QPainter对象一定要在栈上创建,别用new。它会在析构时自动做一些清理工作,比如结束绘制状态。我曾经见过有人用new QPainter,结果忘了delete,导致内存泄漏,画面还卡顿。这坑我踩过,你们就别踩了。
4.2 绘制基本图元:点、线、矩形、圆
掌握了画笔,咱们就开始画点东西。QPainter提供了丰富的绘制函数,咱们挑最常用的几个说说。
4.2.1 点
画点最简单,用drawPoint()。但注意,点的大小由画笔的宽度决定。如果你画了一个宽度为1的画笔,点就是一个像素;宽度为5,点就是一个5x5的方块。
painter.drawPoint(50, 50); // 画一个点
4.2.2 线
画线用drawLine()。可以传两个点,也可以传四个坐标值。我个人喜欢传四个整数,代码更紧凑。
painter.drawLine(10, 10, 200, 10); // 画一条水平线
4.2.3 矩形
画矩形用drawRect()。它接受一个QRect对象,或者直接传x, y, width, height。如果你想要圆角矩形,用drawRoundedRect()。
painter.drawRect(50, 50, 100, 80); // 画一个矩形
4.2.4 圆(椭圆)
画圆其实是用drawEllipse()。它画的是椭圆,如果你传的宽高相等,那就是正圆。
painter.drawEllipse(100, 100, 50, 50); // 画一个半径为50的圆
4.3 画笔与画刷设置
光画出来还不够,你得控制线条的颜色、粗细、样式,以及填充区域的纹理。这就轮到QPen和QBrush登场了。
4.3.1 QPen:控制线条
QPen控制线条的样式。你可以设置颜色、宽度、线型(实线、虚线、点线等)。
QPen pen;
pen.setColor(Qt::red);
pen.setWidth(3);
pen.setStyle(Qt::DashLine); // 虚线
painter.setPen(pen);
我个人习惯,在绘制前先设置好画笔,然后一口气画完。别画一笔改一次,那样性能会下降。你想想看,每次setPen都会触发内部状态更新,频繁调用肯定慢。
4.3.2 QBrush:控制填充
QBrush控制填充区域的颜色和纹理。可以是纯色、渐变、或者图片纹理。
QBrush brush;
brush.setColor(Qt::blue);
brush.setStyle(Qt::SolidPattern); // 纯色填充
painter.setBrush(brush);
如果你想要渐变填充,可以用QLinearGradient或QRadialGradient。这个在画一些高级图形时特别有用,比如模拟光照效果。
painter.setPen(Qt::NoPen)或painter.setPen(QPen())来确保状态干净。
4.4 抗锯齿与渲染优化
好了,图形能画了,颜色也能控制了。但你会发现,画出来的线条边缘有锯齿,尤其是斜线或圆。这就是所谓的“锯齿效应”。怎么解决?抗锯齿。
4.4.1 开启抗锯齿
QPainter提供了setRenderHint()方法。你只需要在绘制前调用一下:
painter.setRenderHint(QPainter::Antialiasing, true);
就这么简单。开启后,线条边缘会变得平滑。但代价是性能会下降一点点。为什么?因为抗锯齿需要计算每个像素的透明度,计算量大了。
我建议:只在需要高质量输出的地方开启抗锯齿。比如画圆、画斜线时。画水平或垂直线时,其实没必要,因为本身就没有锯齿。
4.4.2 其他渲染优化
除了抗锯齿,还有几个优化点值得注意:
- 减少绘制调用次数: 能用
drawPoints()一次画多个点,就别用drawPoint()循环画。QPainter内部对批量绘制有优化。 - 使用QPixmap缓存: 如果某个图形不经常变化,可以把它画到一个QPixmap上,然后在paintEvent里直接贴图。这比每次都重新绘制快得多。
- 避免在paintEvent里做复杂计算: paintEvent应该只负责绘制,别在里面做排序、查找等操作。把这些计算放到外面,绘制时直接用结果。
QPainter::HighQualityAntialiasing(Qt 5.0之后已废弃,用QPainter::Antialiasing即可),并配合setRenderHint(QPainter::SmoothPixmapTransform, false)来平衡质量与性能。
4.5 实战小例子:画一个带边框的圆
说了这么多,咱们来个小例子。画一个红色的圆,蓝色边框,开启抗锯齿。
void MyWidget::paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
// 设置画笔:蓝色,宽度2,实线
QPen pen(Qt::blue, 2, Qt::SolidLine);
painter.setPen(pen);
// 设置画刷:红色,纯色填充
QBrush brush(Qt::red, Qt::SolidPattern);
painter.setBrush(brush);
// 画圆
painter.drawEllipse(50, 50, 100, 100);
}
这段代码跑起来,你会看到一个漂亮的红色圆,边缘平滑,没有锯齿。嗯,这就是QPainter的基本功。
好了,这一章就到这里。下一章咱们聊聊更高级的图形变换,比如旋转、缩放、平移。这些在EPS软件里经常用到,比如旋转一个图形元素。到时候见!