为什么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。
我正在尝试用 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。