Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Why does QPainter.fillRect with transparency leave artifacts?



  • main.cpp:

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    
    #include <QBuffer>
    #include <QImage>
    #include <QPainter>
    #include <QQmlContext>
    
    class ImageSource : public QObject
    {
        Q_OBJECT
    
    public:
        ImageSource(QObject* parent=nullptr)
            : QObject(parent)
        {
    
        }
    
        Q_INVOKABLE QString genImageString(QString text){
            QImage textimage(64,64, QImage::Format_RGBA8888);
    
            // create image
            QPainter painter(&textimage);
            //painter.setPen(QColor(255,0,0,255));
            //painter.fillRect(0,0,64,64, QColor(0,0,0,255));
            painter.fillRect(0,0,64,64, QColor(0,0,0,0));
            //painter.fillRect(0,0,64,64, QBrush(Qt::transparent, Qt::BrushStyle::SolidPattern));
            painter.setPen(Qt::blue);
            painter.setFont(QFont("Arial", 20));
            painter.drawText(QRect(0,0,64,64), Qt::AlignCenter, text);
    
            QByteArray bArray;
            QBuffer buffer(&bArray);
            buffer.open(QIODevice::WriteOnly);
    
            textimage.save(&buffer, "PNG");
    
            QString image("data:image/png;base64,");
            image.append(QString::fromLatin1(bArray.toBase64().data()));
    
            return image;
        }
    };
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
    
        ImageSource is;
        auto context = engine.rootContext();
        context->setContextObject(&is);
    
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    
    #include "main.moc"
    

    main.qml:

    import QtQuick 2.12
    import QtQuick.Window 2.12
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("QML Image from Source String")
    
    
        GridView {
            id: gridview
    
            anchors.fill: imagetester
    
            model: 9
            cellWidth: imagetester.width/3
            cellHeight: imagetester.height/3
    
            delegate: Rectangle {
                width: gridview.cellWidth
                height: gridview.cellHeight
                color: !(index % 2) ? "lightgrey" : "grey"
                opacity: 0.75
            }
        }
    
        Image {
            id: imagetester
    
            width: 64
            height: 64
            source: genImageString("Hello")
        }
    }
    

    I tried doing a fill with black with alpha set to 255. Then a fill with transparency and it just shows black. Is this an issue with conversion to PNG or the QImage itself?



  • Woah woah woah!!! This is crazy! If I change the image size then it goes away!
    main.cpp:

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    
    #include <QBuffer>
    #include <QImage>
    #include <QPainter>
    #include <QQmlContext>
    
    class ImageSource : public QObject
    {
        Q_OBJECT
    
    public:
        ImageSource(QObject* parent=nullptr)
            : QObject(parent)
        {
    
        }
    
        Q_INVOKABLE QString genImageString(QString text, int width, int height){
            QImage textimage(width,height, QImage::Format_RGBA8888);
    
            // create image
            QPainter painter(&textimage);
            //painter.setPen(QColor(255,0,0,255));
            //painter.fillRect(0,0,width,height, QColor(0,0,0,255));
            painter.fillRect(0,0,width,height, QColor(0,0,0,0));
            //painter.fillRect(0,0,width,height, QBrush(Qt::transparent, Qt::BrushStyle::SolidPattern));
            painter.setPen(Qt::blue);
            painter.setFont(QFont("Arial", 20));
            painter.drawText(QRect(0,0,width,height), Qt::AlignCenter, text);
    
            QByteArray bArray;
            QBuffer buffer(&bArray);
            buffer.open(QIODevice::WriteOnly);
    
            textimage.save(&buffer, "PNG");
    
            QString image("data:image/png;base64,");
            image.append(QString::fromLatin1(bArray.toBase64().data()));
    
            return image;
        }
    };
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
    
        ImageSource is;
        auto context = engine.rootContext();
        context->setContextObject(&is);
    
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    
    #include "main.moc"
    

    main.qml:

    import QtQuick 2.12
    import QtQuick.Window 2.12
    
    Window {
        visible: true
        width: iw
        height: ih
        title: qsTr("QML Image from Source String")
    
    
        property int iw: 256
        property int ih: iw
    
        GridView {
            id: gridview
    
            anchors.fill: imagetester
    
            model: 9
            cellWidth: imagetester.width/3
            cellHeight: imagetester.height/3
    
            delegate: Rectangle {
                width: gridview.cellWidth
                height: gridview.cellHeight
                color: !(index % 2) ? "lightgrey" : "grey"
                opacity: 0.75
            }
        }
    
        Image {
            id: imagetester
    
            width: iw
            height: ih
            source: genImageString("Hello", width, height)
        }
    }
    

    64x64:
    artifacts64.png
    128x128:
    noartifacts128.png



  • I can reduce to 32px and there are not any artefacts. Possibly an issue with your graphics driver?

    My machine specs;
    Windows 64bit Ultimate
    16GB RAM
    Intel i7 ~ 2.2gHz
    nVidia 1GB ~ GT555M



  • I have Qt apps that display icons from other sources that don't do that. I should see if dumping it directly to file will have images with artifacts. Then I know if its in the qml render side, or in the output of the function that does the drawing.



  • Interesting, the png file that gets written has the same artifacts. So that limits the issue to the painting/image conversion code:

    Q_INVOKABLE QString genImageString(QString text, int width, int height){
            QImage textimage(width,height, QImage::Format_RGBA8888);
    
            // create image
            QPainter painter(&textimage);
            //painter.setPen(QColor(255,0,0,255));
            //painter.fillRect(0,0,width,height, QColor(0,0,0,255));
            painter.fillRect(0,0,width,height, QColor(0,0,0,0));
            //painter.fillRect(0,0,width,height, QBrush(Qt::transparent, Qt::BrushStyle::SolidPattern));
            painter.setPen(Qt::blue);
            painter.setFont(QFont("Arial", 20));
            painter.drawText(QRect(0,0,width,height), Qt::AlignCenter, text);
    
            QByteArray bArray;
            QBuffer buffer(&bArray);
            buffer.open(QIODevice::WriteOnly);
    
            textimage.save(&buffer, "PNG");
    
            QFile pngfile(QString("image%1%2.png").number(width).number(height));
            if(pngfile.open(QIODevice::WriteOnly)){
                textimage.save(&pngfile, "PNG");
            }
            pngfile.close();
    
            QString image("data:image/png;base64,");
            image.append(QString::fromLatin1(bArray.toBase64().data()));
    
            return image;
        }
    


  • Okay, found something related:
    https://bugreports.qt.io/browse/QTBUG-16264?focusedCommentId=201740&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-201740

    I most likely need to spend some time learning about the different modes.

    Q_INVOKABLE QString genImageString(QString text, int width, int height){
            QImage textimage(width,height, QImage::Format_RGBA8888);
    
            // create image
            QPainter painter(&textimage);
            //painter.setPen(QColor(255,0,0,255));
            //painter.fillRect(0,0,width,height, QColor(0,0,0,255));
            painter.setCompositionMode(QPainter::CompositionMode_Clear);
            painter.fillRect(0,0,width,height, QColor(0,0,0,0));
            //painter.fillRect(0,0,width,height, QBrush(Qt::transparent, Qt::BrushStyle::SolidPattern));
            painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
            painter.setPen(Qt::blue);
            painter.setFont(QFont("Arial", 20));
            painter.drawText(QRect(0,0,width,height), Qt::AlignCenter, text);
    
            QByteArray bArray;
            QBuffer buffer(&bArray);
            buffer.open(QIODevice::WriteOnly);
    
            textimage.save(&buffer, "PNG");
    
            QFile pngfile(QString("image%1%2.png").number(width).number(height));
            if(pngfile.open(QIODevice::WriteOnly)){
                textimage.save(&pngfile, "PNG");
            }
            pngfile.close();
    
            QString image("data:image/png;base64,");
            image.append(QString::fromLatin1(bArray.toBase64().data()));
    
            return image;
        }
    

Log in to reply