Search code examples
qtmaskqpainter

How to use a mask with QPainter?


I have a shape (in blue) loaded from a PNG with transparency:

enter image description here

Then I'm drawing several circles on top of this shape (in red) with QPainter::drawEllipse.

enter image description here

The result of that is somewhat similar to the third picture with the red shape completely covering the blue one:

enter image description here

What I would like however is for the blue shape to act as a mask to the red one, with a result like this:

enter image description here

Is it possible to do that with QPainter?


Solution

  • It's possible. Assuming you're loading your PNG into a QImage, you can do something like this to create a mask from your image:

    QImage img("your.png");
    QPixmap mask = QPixmap::fromImage(img.createAlphaMask());
    

    See the other to create*Mask functions in QImage for alternatives.

    Then it's a simple matter of setting the painter's clip region:

    QPainter p(this);
    p.setClipRegion(QRegion(mask));
    

    Here's a stupid demo (don't use that code as-is, the image loading, mask and region creation should be cached, they are potentially expensive):

    #include <QtGui>
    
    class W: public QWidget
    {
        Q_OBJECT
        public:
            W(): QWidget(0) { }
    
        protected:
            void paintEvent(QPaintEvent *)
            {
                QPainter p(this);
                QImage img("../back.png");
                QPixmap mask = QPixmap::fromImage(img.createAlphaMask());
    
                // draw the original image on the right
                p.drawImage(300, 0, img);
    
                // draw some ellipses in the middle
                p.setBrush(Qt::red);
                for (int i=0; i<100; i+=10)
                    p.drawEllipse(i+150, i, 20, 70);
    
                // and do the same thing, but with the mask active
                p.setClipRegion(QRegion(mask));
                for (int i=0; i<100; i+=10)
                    p.drawEllipse(i, i, 20, 70);
            }
    };
    

    Which produces something like this: enter image description here