参考:
C++ GUI Programming with Qt 4, Second Edition
本地环境:
win10专业版,64位,Qt 5.12
上一篇:
qt5-入门-2D绘图-基础-CSDN博客
https://blog.csdn.net/pxy7896/article/details/138284671
坐标系
QPainter默认坐标原点在屏幕左上角,水平向右是x增长的方向,垂直向下是y增长的方向。每个像素占据了一个1x1的方块。这样的坐标系其实是 half-pixel,指像素(x, y)的中心其实是在(x + 0.5, y + 0.5)上。比如要在(0, 0)绘制一个像素,其实是像素的左上角在(0, 0),因此这个像素的中心在(0.5, 0.5)。
上一节提到反走样,那个可以让曲线变得更平滑,但是反走样本身是需要一定的计算量的,所以默认是关闭的。而且反走样因为增加了像素,所以是无法精确控制到某一个像素的。
只有在禁止反走样时,才有0.5像素的偏移。如果使用了反走样,实际上,为了使(x, y)处产生一个像素,实际会在这个点的四方( (x-0.5, y-0.5)、(x-0.5, y+0.5)、(x+0.5, y-0.5)、(x+0.5, y+0.5) )各绘制一个像素,只是颜色/亮度不同。
下面比较一下关闭与开启反走样带来的区别:
可以看到,当把起点平移0.5像素后,pen width = 1和3时,反走样开启与否带来的差异已经被消除了。
viewport & window
viewport与window机制可以绘制与绘图设备的大小和分辨率无关的图形。使用QPainter绘制时,传给QPainter的是逻辑坐标,Qt会将逻辑坐标转换成物理坐标然后绘制。
逻辑坐标是window用的,物理坐标是viewport用的。
通常,视口和窗口的坐标是一致的。比如一个600 x 800的 widget,默认情况下,视口和窗口都是一个320 x 200的矩形,原点都在(0, 0),此时,视口和窗口的坐标是相同的。
但如果使用setWindow做这样的设置:painter.setWindow(-50, -50, 100, 100);
就表示将(-50, -50)对应到物理坐标的(0, 0),后两个参数指明了window的长和高,虽然逻辑坐标系下是长100高100,但实际在物理坐标系中表示长320,宽200。
那么可以用比例简单算一下,如上图所示。
QPainter还提供一个“世界坐标转换”,但是转变的是坐标系。可以平移、旋转、剪切什么的。
用QTransform
的好处是,当有很多个图形需要做同一套变换时,可以设好一个transform,直接套用。如果只有一个需要变换的话,完全可以用painter.translate(-50.0, -50.0);
这样的语句。
void paintEvent(QPaintEvent *event) {
QPainter painter(this);
QFont font("Courier", 24);
painter.setFont(font);
painter.drawText(50, 50, "Test");
QTransform transform;
transform.translate(+50.0, +50.0);
transform.rotate(+45.0);
painter.setWorldTransform(transform);
painter.drawText(50, 50, "Test");
}
绘图设备
绘图设备是指继承了QPainterDevice
的子类,包括QPixmap
QBitmap
QImage
QPicture
:
QPixmap
是专为图像设计的,针对图像的显示做了优化。是使用底层的绘制系统绘制的,无法提供像素级别的操作// 可以将图像绘制到指定位置 QPainter painter(this); QPixmap pixmap("Cat.png"); painter.drawPixmap(10, 10, 128, 128, pixmap);
QBitmap
是QPixmap
的子类,只有黑白两种颜色QImage
为图像的像素级访问做了优化,是独立于硬件的绘制系统,是跨平台的QPicture
可以记录和重现QPainter
的各条命令,是与平台无关的QPicture picture; QPainter painter; painter.begin(&picture); // paint in picture QFont font("Courier", 24); painter.setFont(font); painter.drawText(50, 50, "Test"); painter.end(); picture.save("drawing.pic"); // 如果要重现 QPicture picture; picture.load("path/drawing.pic"); // load picture QPainter painter(this); // 我是在paintEvent中用的,所以写了this painter.begin(&picture); // paint in picture painter.drawPicture(0, 0, picture); // draw the picture at (0,0) painter.end();