QImage 如何设置图片的透明度

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::ImageShow和 DrawTool::DrawToolData,是为了方便保存一些图像绘制过程中的变量。可以直接理解为成员变量。也可以参考我的上一篇博客 Qt QImage scaled方法缩放中的问题, 里面有相应的解释。

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

相关推荐

备孕怎样生女孩儿几率大?成功怀上女孩有什么秘诀?
殷勤的近义词
365bet娱乐网站

殷勤的近义词

📅 10-02 👁️ 7219
杭州数据中心
体育外围app网站365

杭州数据中心

📅 09-06 👁️ 4589
手机卡丢失了怎么办?挂失与补办全攻略来了
体育外围app网站365

手机卡丢失了怎么办?挂失与补办全攻略来了

📅 08-22 👁️ 5722
魔兽世界,次级好运符有什么用?(魔兽世界次级好运符怎么用)
亚博和365是一家的吗

魔兽世界,次级好运符有什么用?(魔兽世界次级好运符怎么用)

📅 07-25 👁️ 5221
第19届 2010年南非世界杯
亚博和365是一家的吗

第19届 2010年南非世界杯

📅 07-10 👁️ 6372