使用scanLine修改QImage 时,出现伪影的原因是什么?

huangapple go评论73阅读模式
英文:

When modifying a QImage using scanLine, artifacts show up. Why?

问题

这段代码在输出中生成了奇怪的图像伪影,这是由于paint函数的渲染方法引起的。它在渲染图标时使用了源SVG图像的不透明度(alpha通道),这可能导致额外的矩形部分。要解决这个问题,您可以尝试以下方法:

class Icon {
public:
    // ...

    /// renders a pixmap that uses primary where the mask is black,
    /// secondary where the mask is white. Mask defines transparency.
    QPixmap paint(const QColor &primary, const QColor &secondary) const {
        QImage buffer(mask.size(), QImage::Format_ARGB32_Premultiplied);
        Gradient gradient{primary.rgb(), secondary.rgb()};
        
        for (int y = 0; y < mask.height(); ++y) {
            const QRgb *srcLine = reinterpret_cast<const QRgb*>(mask.constScanLine(y));
            QRgb *destLine = reinterpret_cast<QRgb*>(buffer.scanLine(y));
            
            for (int x = 0; x < mask.width(); ++x) {
                const QRgb &src = srcLine[x];
                int gray = qGray(src); // Calculate grayscale value
                int alpha = qAlpha(src);
                destLine[x] = gradient.at(gray, alpha); // Use grayscale as index
            }
        }

        // Create a new image with transparency
        QImage finalImage(mask.size(), QImage::Format_ARGB32_Premultiplied);
        finalImage.fill(Qt::transparent);

        QPainter finalPainter(&finalImage);
        finalPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
        finalPainter.drawImage(0, 0, buffer);

        QPixmap pixmap;
        pixmap.convertFromImage(finalImage);
        return pixmap;
    }

    // ...
};

通过这种方式,您首先计算源SVG图像的灰度值,并使用该值作为索引来获得渐变颜色,然后将它们合成到一个具有透明度的最终图像中。这应该可以解决生成额外矩形部分的问题。

英文:

My application has different color modes. Therefore, I need to render my icons in different colors.

My icons are grayscale SVGs. Each color mode defines two colors for icons. One color shall replace black, the other shall replace white.

The code I use to implement this is as follows:

struct Gradient {
    QRgb a, b;

    /// index in [0, 255]. alpha from mask modifies dest alpha.
    QRgb at(int index, int alpha) const {
        const int rindex = 255 - index;
        return qRgba(
            (qRed(a) * rindex + qRed(b) * index) / 255,
            (qGreen(a) * rindex + qGreen(b) * index) / 255,
            (qBlue(a) * rindex + qBlue(b) * index) / 255,
            (((qAlpha(a) * rindex + qAlpha(b) * index) / 255) * alpha) / 255
        );
    }
};

class Icon {
public:
    explicit Icon(const QString &amp;path): path{path} {}

    /// must set once before rendering pixmaps.
    /// generates a mask image from the source SVG with the given size.
    void setSize(QSize size) {
        if (mask.size() != size) {
            QSvgRenderer renderer(path);
            if (renderer.isValid()) {
                mask = QImage(size, QImage::Format_ARGB32_Premultiplied);
                mask.fill(Qt::transparent);
                QPainter svgPainter(&amp;mask);
                renderer.render(&amp;svgPainter);
            }
        }
    }

    /// renders a pixmap that uses primary where the mask is black,
    /// secondary where the mask is white. Mask defines transparency.
    QPixmap paint(const QColor &amp;primary, const QColor &amp;secondary) const {
        QImage buffer(mask.size(), QImage::Format_ARGB32_Premultiplied);
        Gradient gradient{primary.rgb(), secondary.rgb()};
        for (int y = 0; y &lt; mask.height(); ++y) {
            const QRgb *srcLine = reinterpret_cast&lt;const QRgb*&gt;(mask.constScanLine(y));
            QRgb *destLine = reinterpret_cast&lt;QRgb*&gt;(buffer.scanLine(y));
            for (int x = 0; x &lt; mask.width(); ++x) {
                const QRgb &amp;src = srcLine[x];
                // using red as indicator for the grayscale color.
                destLine[x] = gradient.at(qRed(src), qAlpha(src));
            }
        }
        QPixmap pixmap;
        pixmap.convertFromImage(buffer);
        return pixmap;
    }

    bool isNull() const {
        return mask.isNull();
    }
private:
    QString path;
    QImage mask;
};

This code generates strange artifacts in the output:

使用scanLine修改QImage 时,出现伪影的原因是什么?

From this input:

使用scanLine修改QImage 时,出现伪影的原因是什么?

This is rendered with primary and secondary colors both #657590. The dark blue is the backing widget's background color. The original icon just has the cog outline.

Why are the additional rectangular parts created? They are not part of the source image. I tried to use buffer.setPixel instead of scanLine, but that produced the same output.

答案1

得分: 0

问题出在图像格式QImage::Format_ARGB32_Premultiplied上。我修改图像的方式,QImage::Format_ARGB32 更合适。使用这种格式解决了问题。

英文:

The problem was the image format QImage::Format_ARGB32_Premultiplied. The way I modified the image, QImage::Format_ARGB32 was appropriate. Using that format solved the problem.

huangapple
  • 本文由 发表于 2023年2月8日 23:50:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75388321.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定