当前位置: 首页 > article >正文

QImage 如何设置图片的透明度

最近遇到了一些这样的需求,在窗口可以调节显示图片的透明度,但是不能影响其他图片。一个窗口显示的图片并不是一张,而是多张通过绘制的形式叠加起来的。可以理解为类似图层。

就像下面这个组合一样,想法是在拖动右侧透明度的滑条的时候,只修改上层图像的透明度,并不会修改底层背景图的透明度。

先看下预期效果:
在这里插入图片描述

1、通过 QPainter::CompositionMode_DestinationIn 模式模拟显示透明度

偶然的通过了解 QPainter::CompositionMode 的过程中发现了 QPainter::CompositionMode_DestinationIn,帮助文档中解释为:

The output is the destination, where the alpha is reduced by that of the source. This mode is the inverse of CompositionMode_SourceIn.

输出的是目标,并且其alpha的值与绘制源的相关。初次就有了下面的想法:

void ImageWidget::paintEvent(QPaintEvent *event)
{
    QSize sizeImg = size();
    QImage imgBack(sizeImg, QImage::Format_ARGB32);
    imgBack.fill(Qt::black);

    DrawTool::ImageShow img = m_img;
    DrawTool::DrawToolData op = m_operate;

    QImage imgDraw(img.size, QImage::Format_ARGB32);
    imgDraw.fill(Qt::transparent);
    m_draw = imgDraw.scaled(img.size * m_dScale, Qt::KeepAspectRatio, Qt::FastTransformation);

    QPainter painter;
    painter.begin(&imgBack);
    painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    painter.drawImage(m_paintPt, m_show);
    painter.drawImage(m_paintPt, m_mask);
    painter.drawImage(m_paintPt, m_draw);

    painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
    painter.fillRect(imgBack.rect(), QColor(0, 0, 0, op.diaphaneity));
    painter.end();

    painter.begin(this);
    painter.setCompositionMode(QPainter::CompositionMode_Source);
    painter.drawImage(QPoint(0, 0), imgBack);
    painter.end();
}

上面这个绘制函数的过程是,先在一张纯黑的图像上以 QPainter::CompositionMode_SourceOver 的模式绘制三张图像,然后修改 QPainter 的 CompositionMode 为 QPainter::CompositionMode_DestinationIn,绘制并填充一个矩形,填充的颜色设定了我们前面说的透明度,并且这个透明度的值是可以动态修改的。

经过测试发现,这样的操作会将整个窗口的透明度都修改。效果如下:
在这里插入图片描述
虽然只是修改了窗口的透明程度,并不会实际的修改图片的通明度。但与我们的目标想不好像多了一点。所以这并不是最佳答案。显然得重新找答案了。

2、修改源图像的像素颜色透明度

既然是要修改图片的透明度,那么我们是不是可以直接修改图片每个像素的透明度呢?

void ImageWidget::setImageAlpha(QImage& img, int val)
{
    for (int r = 0, wd = img.width(); r < wd; ++r)
    {
        for (int c = 0, ht = img.height(); c < ht; ++c)
        {
            QColor color = img.pixelColor(r, c);
            if (color == Qt::transparent)
            {
                continue;
            }
            color.setAlpha(val);
            img.setPixelColor(r, c, color);
        }
    }
}

像上面这样,每次在绘制的时候先修改目标图片的透明度,然后再将图片绘制到屏幕上。

if (oldOp.diaphaneity != newOp.diaphaneity)
{
    DrawTool::ImageShow img = m_img;
    QImage maskImg = m_maskOri;
    setImageAlpha(maskImg, newOp.diaphaneity);
    m_mask = maskImg.scaled(img.size * m_dScale, Qt::KeepAspectRatio, Qt::FastTransformation);
}

这种情况下,绘制过程中设置绘制模式为 QPainter::CompositionMode_DestinationIn 并填充矩形框的操作就没有什么实际的意义了。需要注释掉。

//painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
//painter.fillRect(imgBack.rect(), QColor(0, 0, 0, op.diaphaneity));

测试之后,发现这种想法能够满足我们的目标。并且绘制过程也很丝滑。效果如下:

在这里插入图片描述

但是在后续的测试中发现,通过上面这种粗暴的方式设置了源图的透明度之后,再重新从图中读取像素颜色,像素颜色会有一定的误差。并且和透明度的大小存在一定的关系,但不是线性的。

当透明度在 50% 左右的时候,我发现,颜色会有一种这样的关系:

newRgb = 255 -255 - rgb)*double)a / 255

这个公式对R、G、B都适用,计算出来的结果有误差(是因为有了浮点数),但不影响。但是如果当a越小或者越大的时候,这个误差会增加。因此这个公式肯定是错的。

所以通过这中直接设置源图像透明底的方法,如果设置之后不在乎他的颜色,只是为了显示效果,是完全可以的,但如果它的像素颜色对你有用,或者你要在某些时候恢复它不透明,会有一定的影响。

3、通过 QPainter::CompositionMode_DestinationIn 模式和多个QPainter对象实现

想到这个方法是由上面第一种方式反思得到的,既然可以通过第一种方法模拟得到一个图像的透明度,那么为什么不能通过两个QPainter对象来实现了,一个专门用来实现针对单张图像的透明度,另一个用来实现正常的绘制流程呢?

但是在考虑这种方法的时候,需要注意的一点是:

Qt 中 是不允许两个QPainter对象同时进行绘制的,因此要注意他们之间的绘制顺序关系。

下面这个方法用来绘制一张图像的透明度。

void ImageWidget::drawMaskAlpha(QImage& img)
{
    DrawTool::DrawToolData op = m_operate;
    QImage back(m_mask.size(), QImage::Format_ARGB32);
    back.fill(Qt::transparent);
    img = back;
    QPainter pter(&img);
    pter.setCompositionMode(QPainter::CompositionMode_Source);
    pter.drawImage(0, 0, m_mask);
    pter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
    pter.fillRect(back.rect(), QColor(0, 0, 0, op.diaphaneity));
    pter.end();
}
void ImageBaseWidget::paintEvent(QPaintEvent *event)
{
    QImage imgBack(size(), QImage::Format_ARGB32);
    imgBack.fill(Qt::black);

    DrawTool::ImageShow img = m_img;
    DrawTool::DrawToolData op = m_operate;

    QImage imgDraw(img.size, QImage::Format_ARGB32);
    imgDraw.fill(Qt::transparent);
    m_draw = imgDraw.scaled(img.size * m_dScale, Qt::KeepAspectRatio, Qt::FastTransformation);

    QImage mask;
    drawMaskAlpha(mask);

    QPainter painter;
    painter.begin(&imgBack);
    painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    painter.drawImage(m_paintPt, m_show);
    painter.drawImage(m_paintPt, m_draw);
    painter.drawImage(m_paintPt, mask);
    
    painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
    painter.fillRect(imgBack.rect(), QColor(0, 0, 0, op.diaphaneity));
    painter.end();
    
    painter.begin(this);
    painter.setCompositionMode(QPainter::CompositionMode_Source);
    painter.drawImage(QPoint(0, 0), imgBack);
    painter.end();
}

上面代码中出现的 DrawTool::ImageShowDrawTool::DrawToolData,是为了方便保存一些图像绘制过程中的变量。可以直接理解为成员变量。也可以参考我的上一篇博客 Qt QImage scaled方法缩放中的问题, 里面有相应的解释。

经过测试,最后的这种方法,可以达到我们的目标,并且并不会修改源图,只是在视觉上给我们一种图像透明度的错觉。在实际应用过程中可能会比较实用。


http://www.kler.cn/a/15927.html

相关文章:

  • CSS基础知识01
  • 帽子矩阵--记录
  • SpringBoot - Async异步处理
  • Flutter:input输入框
  • Dubbo 3.2 源码导读
  • 使用c#实现TCP客户端与服务器端
  • kotlin教程4:函数进阶
  • 贪心算法OJ刷题(2)
  • Python并发编程之进程操作
  • aac音频怎么转mp3,这几个方法很简便
  • 2023五一杯B题赛题公布
  • OpenGL(三)——着色器
  • Redis学习笔记01 (数据结构,线程模型,持久化)
  • 分屏视图上线,详情数据秒切换
  • Python小姿势 - # 如何在Python中实现基本的数据类型
  • 如何查看自己是否使用了国产SSL证书?“套牌”SSL证书?
  • GDB 1、超详细的GDB入门笔记,包含演示代码,快速入门
  • Prompt炼丹炉——一系列Prompt自动优化的实践记录
  • python使用公共api下载狗狗图片
  • Cadence基础操作:Schematic编辑
  • vue+element Ui 树型组件tree懒加载+搜索框远程请求数据为平铺类型
  • TF-IDF (BigData, Data Mining)
  • 【LeetCode: 62. 不同路径 | 暴力递归=>记忆化搜索=>动态规划 】
  • 设计模式-适配器模式
  • RabbitMQ 工作队列模式 Work Queue Demo
  • C S S