如何在 Qt/hook QPainter 中渲染没有抗锯齿的 SVG 以避免抗锯齿?

How to render an SVG without antialiasing in Qt/hook QPainter to avoid antialiasing?

本文关键字:SVG Qt hook QPainter      更新时间:2023-10-16

我需要渲染一个仅矢量的SVG,而不在Qt中抗锯齿;具体来说,我不希望生成的图像包含任何与源文件中指定的颜色不完全一样的颜色。

QImage中加载SVG时,Qt默认使用抗锯齿;即使使用显式QSvgRenderer::renderQPainter::Antialiasing渲染提示也会被忽略。

事实证明这是显而易见的,因为QSVGTinyDocument::draw似乎是实际开始绘图的人,正在强制设置它:

//sets default style on the painter
//### not the most optimal way
mapSourceToTarget(p, bounds);
QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
pen.setMiterLimit(4);
p->setPen(pen);
p->setBrush(Qt::black);
p->setRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::SmoothPixmapTransform);

为了规避这一点,我试图创建一个自定义QPaintEngine包装默认的(然后子类甚至QImagepaintEngine()虚拟方法中返回包装器QPaintEngine),但我找不到任何干净的方法来拦截和更改渲染提示在通往实际QPaintEngine的路上。

QPaintEngine::update()方法似乎是QPaintEngine接收标志的方式,接收对QPaintEngineState的引用,这是一个可怕的笨蛋,只提供getter,而getter又反过来

  • 不是virtual,因此您无法提供实现;
  • 在内部只是假设它实际上是通过粗暴地投射this指针并访问其字段来操作内部子类(QPainterState)的实例。

此外,即使只是提供一个刚刚转发的QPaintEngine也会导致QPainter代码内部出现段错误,所以我很快就停止了。


长话短说:

  • 有没有办法强制禁用/拦截和忽略/...QPainter::AntialiasingQPainter上呈现提示?(QT_NO_ANTIALIASING环境变量不计算在内,因为它仅在没有QT_NO_DEBUG的构建中启用);
  • 更一般地说,如何在抗杀的情况下将 SVG 渲染到QImage

我能想到的唯一解决方案是实现自己的SVG渲染器。使用nanosvg这是微不足道的,但很少有人决定这样做。此外,像几乎所有其他解析器一样,nanosvg不支持每个 SVG 功能(如过滤器等)。无论如何,这里有一个例子。无论您是否要(抗)锯齿,您都将拥有完全控制权。

不是最佳解决方案,但可能会对您有所帮助,方法是使用蒙版删除临时图像中的 Alpha 通道,从而创建锯齿渲染的外观。下面的函数采用您当前渲染抗锯齿QImage,并返回锯齿等效项。

此解决方法对于实时渲染可能不切实际,但如果您只想在预处理步骤中创建QImage,它应该可以正常工作。

QImage convertToAliased(const QImage& source)
{
const auto mask = QPixmap::fromImage(source.createAlphaMask());
QImage image(source.size(), QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter p;
p.begin(&image);
p.setClipRegion(QRegion(mask));
p.drawImage(QPoint(0, 0), source.convertToFormat(QImage::Format_RGB32));
p.end();
return image;
}

代码也可以在 GitHub 中找到。