使用 Qt 绘制像素完美的圆

Posted

技术标签:

【中文标题】使用 Qt 绘制像素完美的圆【英文标题】:Drawing pixel-perfect circles with Qt 【发布时间】:2019-02-20 00:00:52 【问题描述】:

我正在尝试使用QPainter::drawEllipse 来绘制圆圈。我希望能够:

设置圆的笔画宽度(QPen::width) 选择圆心像素的形状(1x11x22x12x2) 可选择填充圆而不是描边 确保圆的半径正确(即使笔划宽度大于 1)

这些目标难以实现。这是我要渲染的示例(手工绘制):

图片为 32x32(放大至 512x512)。红色中心点位于 (15, 15)。中心是1x2,所以在中心像素下方有一个额外的红色像素。笔划的宽度为 2 个像素。如果笔画变宽,像素将被添加到圆圈的内部。无论笔画宽度如何,圆的边界框都是相同的。半径为 8 像素。每条蓝线长 8 个像素。 为了清楚起见,红色和蓝色像素只是用于描述圆圈。它们不是我想要的输出的一部分。

我的问题真正归结为渲染一个完全适合矩形内的椭圆。我可以使用中心点、半径和中心形状来计算矩形。那部分很容易。简单地用这个矩形调用drawEllipse 是行不通的。我想我必须在调用drawEllipse 之前以某种方式调整这个矩形,但我不太确定如何调整它。我试过摆弄它,我找到了一些适用于某些笔宽但不适用于其他笔宽的解决方案。

笔帽重要吗?我一直在使用RoundCap。我应该使用不同的上限吗?

我几乎要考虑自己进行像素操作了。我正在渲染到QImage 并使用Source 复合操作,因此我的代码可能比drawEllipse 稍快。 memsetQImage::fill 快大约 10 倍,因此编写更快的代码可能不会太难!不过,我宁愿不必这样做。

【问题讨论】:

您能否向我们展示一些您尝试过但不完全符合要求的代码?这可能会给你一些更好的答案。 【参考方案1】:

我偶然发现了一个section in the docs,它谈到了QRects 是如何呈现的。它描述了渲染像素和逻辑矩形之间的关系。渲染的矩形大于逻辑矩形。我所要做的就是缩小逻辑矩形以进行补偿。

QRect adjustStrokedRect(const QRect rect, const int thickness) 
  return QRect
    rect.left() + thickness / 2,
    rect.top() + thickness / 2,
    rect.width() - thickness,
    rect.height() - thickness
  ;

好的,现在我可以在正确的位置渲染描边矩形。 QRect 描述了一个椭圆,那么如果我只是将这个变换应用于那个矩形呢?

没有。

如果厚度是1, 2, 4, 6 而不是3, 5, 7,它会起作用。当厚度为3, 5, 7 时,圆圈太小了一个像素。因此,如果thickness % 2 == 1 && thickness != 1,我尝试将1 添加到矩形大小,但随后从正方形呈现非对称圆形。对于某些位置和大小的组合,即使大小为正方形,也会呈现出不稳定的不对称圆形。

这是一个您可以轻松复制的奇怪图像:

使用以下代码生成它:

QImage image32, 32, QImage::Format_ARGB32_Premultiplied;
QPainter painterℑ
QPen penQt::NoBrush, 3.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin;
pen.setColor(QColor0, 255, 0, 255);
painter.setPen(pen);
painter.drawEllipse(8, 8, 17, 17);
image.save("weird.png");

我简直不明白这怎么可能。对我来说,drawEllipse 似乎正在渲染一个大致适合矩形的椭圆。我无法在文档中的任何地方找到矩形和椭圆之间的关系。也许这是因为这是一种非常松散的关系。

QPainter::drawEllipse 绘制笔画宽度为1 的圆圈没有问题,所以现在我不允许在我的应用程序中使用粗圆圈。如果我不能完美地渲染它,我根本不会渲染它。我没有将此答案标记为已接受,因为我仍然希望它能够正常工作。

【讨论】:

【参考方案2】:

我可能为时已晚,但仍以供将来参考:

不幸的是,Qt 使用贝塞尔曲线绘制椭圆(截至目前,它可能很快就会改变),这是一个很好的椭圆近似,但并不完美。绘制像素完美的椭圆需要在像素级别手动实现。

【讨论】:

以上是关于使用 Qt 绘制像素完美的圆的主要内容,如果未能解决你的问题,请参考以下文章

在片段着色器中绘制别名像素完美线?

Opengl像素完美2D绘图

使用Python,OpenCV对图像进行亚像素点检测,并拟合椭圆进行绘制

Qt 绘制的内容丢失了

C++ 2D 像素完美碰撞检测库?

使用 OpenGL 进行像素完美的 2D 渲染