为什么QPainter::drawPoint画一条水平线段?

Why does QPainter::drawPoint draw a horizontal line segment?

我正在尝试用 QPainter 绘制一个 3 像素的大点。但是下面的代码反而绘制了一条宽度为 3 像素的水平线。

#include <QPainter>
#include <QImage>

int main()
{
    const int w=1000, h=1000;
    QImage img(w, h, QImage::Format_RGBX8888);
    {
        QPainter p(&img);
        p.fillRect(0,0,w,h,Qt::black);
        p.scale(w,h);
        p.setPen(QPen(Qt::red, 3./w, Qt::SolidLine, Qt::RoundCap));
        p.drawPoint(QPointF(0.1,0.1));
    }
    img.save("test.png");
}

这是生成图像的左上角:

我希望得到一个红色圆圈点,或者至少是一个正方形 - 但我得到的是这条线段。如果我注释掉 p.scale(w,h) 并在位置 (100,100) 处绘制宽度为 3 的点(而不是 3./w),那么我会得到一个小的高度对称的 3 像素,宽度点。

这是怎么回事?为什么我得到的是线段而不是预期的点?以及如何修复它,而不求助于绘制椭圆或避免 QPainter::scale?

我在 Linux x86 和 g++ 5.5.0 上使用 Qt 5.10.0。 Qt 5.5.1 也是如此。

虽然我还不能准确指出问题发生的原因,但我已经相对接近解决方案了。问题在于缩放。我用下面的代码用不同的缩放比例和宽度做了很多试验和错误。

const int w=500, h=500;
const int scale = 100;
float xPos = 250;
float yPos = 250;
float widthF = 5;
QImage img(w, h, QImage::Format_RGBX8888);
{
    QPainter p(&img);
    p.setRenderHints(QPainter::Antialiasing);
    p.fillRect(0,0,w,h,Qt::black);
    p.scale(scale, scale);
    p.setPen(QPen(Qt::red, widthF/(scale), Qt::SolidLine, Qt::RoundCap));
    p.drawPoint(QPointF(xPos/scale, yPos/scale));
}
img.save("test.png");

以上代码生成图像

我的观察是
1) 由于高度缩放,点(只有 3 个像素宽)无法在较低宽度(点的宽度)下按比例缩放,如果将宽度设置为 30 之类的东西,圆形是可见的。
2)如果你想保持点的宽度低,那么你必须减少缩放比例。
可悲的是,我无法解释为什么在高缩放时它没有按比例扩展。

QPaintEngineEx::drawPoints 似乎将点渲染为长度为 1/63. 的线段。请参阅 Qt 源代码中 qtbase/src/gui/painting/qpaintengineex.cpp 的以下代码:

void QPaintEngineEx::drawPoints(const QPointF *points, int pointCount)
{
    QPen pen = state()->pen;
    if (pen.capStyle() == Qt::FlatCap)
        pen.setCapStyle(Qt::SquareCap);

    if (pen.brush().isOpaque()) {
        while (pointCount > 0) {
            int count = qMin(pointCount, 16);
            qreal pts[64];
            int oset = -1;
            for (int i=0; i<count; ++i) {
                pts[++oset] = points[i].x();
                pts[++oset] = points[i].y();
                pts[++oset] = points[i].x() + 1/63.;
                pts[++oset] = points[i].y();
            }
            QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint);
            stroke(path, pen);
            pointCount -= 16;
            points += 16;
        }
    } else {
        for (int i=0; i<pointCount; ++i) {
            qreal pts[] = { points[i].x(), points[i].y(), points[i].x() + qreal(1/63.), points[i].y() };
            QVectorPath path(pts, 2, 0);
            stroke(path, pen);
        }
    }
}

注意不透明画笔分支中的 pts[++oset] = points[i].x() + 1/63.; 行。这是路径的第二个顶点 - 相对于点的所需位置移动。

这解释了为什么线会延伸到请求位置的右侧,以及为什么它取决于比例尺。因此,对于理想的 QPainter 实现,OP 中的代码似乎并没有错,只是遇到了一个 Qt 错误(无论是在方法的实现中还是在其文档中)。

所以结论:必须通过使用不同的比例、绘制椭圆或绘制长度比 [=15= 小得多的线段来解决这个问题] 确实如此。

我已将此报告为 QTBUG-70409