在 QT 中用完全透明的笔“绘图”

Posted

技术标签:

【中文标题】在 QT 中用完全透明的笔“绘图”【英文标题】:"Drawing" with a totally transparent pen in QT 【发布时间】:2021-05-31 04:03:08 【问题描述】:

我正在用 QT 编写一个白板应用程序。我使用的是双层方法,所以我有一个包含绘图的 QPixmap,另一个包含背景。毫无疑问,绘图像素图带有 alpha 通道。

现在我想实现一个橡皮擦工具。无论在何处绘制,此工具都应将像素图的颜色恢复为QColor(255, 255, 255, 0)(即,完全透明)。然而,我的绘画方法并不适合这一点。

这是我的绘图程序:

void WhiteBoardWidget::draw(QPointF pos, bool erase) 
    QPainter painter( &underlyingImage );
    QPen pen = painter.pen();

    if( ! erase ) 
        pen.setColor(penColor);
        pen.setWidth(penWidth);
     else 
        pen.setColor( QColor(255, 255, 255, 0) );
        pen.setWidth(penWidth * EraserSizeFactor);
    

    painter.setPen(pen);
    painter.drawLine( lastPoint, pos );
    lastPoint = pos;
    update();

我明白为什么它不起作用(透明笔不会改变像素图,因为它是,等待它......透明)。我只是不确定什么是做我想做的事的好方法。

【问题讨论】:

【参考方案1】:

这是正在使用的QPainter 中的composition mode 的问题。默认为QPainter::CompositionMode_SourceOver,因为当前的笔是透明的,所以它会保持原样离开地下。通过设置为QPainter::CompositionMode_Clear,您可以强制画家擦除任何内容。那时您甚至不必更改当前笔的颜色。

【讨论】:

出于隐私原因,确保非 alpha 通道颜色也归零(最好是背景颜色)可能很有用。不确定“_Clear”是否属于这种情况。 虽然如果他将前面的图层绘制到后面的图层上,最终的结果不应该包含旧的非alpha颜色。我想它们只会单独存在于前层! 我不确定是否有某种半透明笔的组合会导致旧图像重新出现。我将改用CompositionMode_Source,它设置所有属性,而不仅仅是透明度。 顺便说一句,代码可以在github.com/Shachar/Whiteboard找到 @ShacharShemesh Source 应该一样好。 Clear 应该可见独立于笔擦除任何内容。但是,可能仍然存在一些隐藏的图像信息(请参阅 Johannes 的评论),例如某些像素为 10、10、10、0,其他像素为 12、12、12、0,您会通过设置Source来避免。【参考方案2】:

基于my previous answer,我将代码翻译成c++

#ifndef DRAWER_H
#define DRAWER_H

#include <QWidget>

enum STATES
    DRAW_STATE,
    CLEAR_STATE
;

class Drawer : public QWidget

    Q_OBJECT
    Q_PROPERTY(STATES currentState READ currentState WRITE setCurrentState NOTIFY currentStateChanged)
public:
    explicit Drawer(QWidget *parent = nullptr);
    STATES currentState() const;
    void setCurrentState(STATES newCurrentState);
Q_SIGNALS:
    void currentStateChanged();

protected:
    void paintEvent(QPaintEvent *);
    void resizeEvent(QResizeEvent *);
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
private:
    QImage background;
    QImage foreground;
    QPoint lastPoint;
    QBrush brushColor;
    int brushSize;
    bool drawing;
    int clearSize;

    STATES m_currentState;
;

#endif // DRAWER_H
#include "drawer.h"

#include <QApplication>
#include <QImage>
#include <QMouseEvent>
#include <QPainter>

Drawer::Drawer(QWidget *parent) :
    QWidget(parent), brushColor(Qt::black), brushSize(2), drawing(false), clearSize(10), m_currentState(DRAW_STATE)

    background = QImage(100, 100, QImage::Format_ARGB32);
    background.fill(Qt::white);
    foreground = QImage(100, 100, QImage::Format_ARGB32);
    foreground.fill(Qt::transparent);

STATES Drawer::currentState() const

    return m_currentState;


void Drawer::setCurrentState(STATES newCurrentState)

    if (m_currentState == newCurrentState)
        return;
    m_currentState = newCurrentState;
    Q_EMIT currentStateChanged();
    if(m_currentState == CLEAR_STATE)
        QPixmap pixmap(QSize(1, 1)* clearSize);
        pixmap.fill(Qt::transparent);
        QPainter painter(&pixmap);
        painter.setPen(QPen(Qt::black, 2));
        painter.drawRect(pixmap.rect());
        painter.end();
        QCursor cursor(pixmap);
        QApplication::setOverrideCursor(cursor);
    
    else if (m_currentState == DRAW_STATE) 
        QApplication::restoreOverrideCursor();
    


void Drawer::paintEvent(QPaintEvent *)

    QPainter painter(this);
    painter.drawImage(QPoint(0, 0), background);
    painter.drawImage(QPoint(0, 0), foreground);


void Drawer::resizeEvent(QResizeEvent *)

    bool changeSize = (width() > foreground.width()) || (height() > foreground.height());
    if(changeSize)
        QSize s(std::max(width(), background.width()), std::max(height(), background.height()));
        background = QImage(s, background.format());
        background.fill(Qt::white);
        QImage newForeground = QImage(s, foreground.format());
        newForeground.fill(Qt::transparent);
        QPainter painter(&newForeground);
        painter.drawImage(QPoint(0, 0), foreground);
        painter.end();
        foreground = newForeground;
    


void Drawer::mousePressEvent(QMouseEvent *event)

    if(event->buttons() & Qt::LeftButton)
        drawing = true;
        lastPoint = event->pos();
        update();
    

void Drawer::mouseMoveEvent(QMouseEvent *event)

    if(event->buttons() & Qt::LeftButton && drawing)
        QPainter painter(&foreground);
        painter.setPen(QPen(brushColor, brushSize, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
        if(m_currentState == CLEAR_STATE)
            QRect r(QPoint(), clearSize*QSize());
            r.moveCenter(event->pos());
            painter.save();
            painter.setCompositionMode(QPainter::CompositionMode_Clear);
            painter.eraseRect(r);
            painter.restore();
        
        else if(m_currentState == DRAW_STATE)
            painter.drawLine(lastPoint, event->pos());
        
        painter.end();
        lastPoint = event->pos();
        update();
    

void Drawer::mouseReleaseEvent(QMouseEvent *event)

    if(event->buttons() & Qt::LeftButton)
        drawing = false;
        update();
    

#include "drawer.h"

#include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>

int main(int argc, char *argv[])

    QApplication a(argc, argv);
    QPushButton *button = new QPushButton("Clear");
    button->setCheckable(true);
    Drawer *drawer = new Drawer;
    QObject::connect(button, &QPushButton::toggled, [drawer, button](bool checked)
        if(checked)
            drawer->setCurrentState(STATES::CLEAR_STATE);
            button->setText("Draw");
        
        else
            drawer->setCurrentState(STATES::DRAW_STATE);
            button->setText("Clear");
        
    );
    QWidget widget;
    QVBoxLayout *lay = new QVBoxLayout(&widget);
    lay->addWidget(button);
    lay->addWidget(drawer);
    widget.resize(640, 480);
    widget.show();
    return a.exec();

【讨论】:

以上是关于在 QT 中用完全透明的笔“绘图”的主要内容,如果未能解决你的问题,请参考以下文章

如何使完全透明视图上的绘图可见

QT下的几种透明效果(QPalette背景白色,窗口设置setWindowOpacity,QPainter使用Clear模式绘图)

具有完全透明背景的 qt 小部件

使用 PixelWriter 在 JavaFX Canvas 上进行透明绘图

在 C/C++ 中用 alpha 值覆盖像素

求助,关于Qt的窗口半透明,窗口上的空间不透明