QPainter类的QPainter::CompositionMode枚举用于说明源图像和目标图像怎样合成显示,一般用于图像的混合,其实在OPenGL中也有混合的技术,和QPainter类的QPainter::CompositionMode大同小异,思想类似。
Qt官方手册Assistant对QPainter::CompositionMode枚举的解释,没有真正在项目中实战用过QPainter::CompositionMode的童鞋来说,理解起来很费劲,网上的资料也是你抄我,我抄你的,真正弄懂、有价值的很少。今天我以示例和OPenGL中有关的阐述来讲解这几个枚举。
目标图像
目标图像是划定的绘制设备像素所在区域(如:窗体、图片或窗体图片的一部分区域)或上次绘制的图像。前者是初次绘制时,如:初次想在窗体区域上绘制,此时窗体区域什么都没有,则目标图像是指窗体、图片中程序划定的要绘制区域所在像素。后者指在窗体或图像上已经绘制过图像。
源图像
源图像是指最新、最近绘制的图像。如:起始绘制了a图像到窗体上(此时窗体所在像素形成的区域是目标图像,a图像是源图像),当a绘制完后,又在a的上面绘制了b图像,则b成为源图像,a成为目标图像。
总结:把将要画上去的图像称为“源图像”,把原来的图像称为“目标图像”。
- QPainter::CompositionMode_SourceOver
该模式是默认模式,使用该模式时,源图像如果和目标图像有重叠,绘制时,则重叠部分是源图像在目标图像上面(源遮挡住目标),重叠部分源的RGBA值和重叠部分目标的RGBA值进行混合,没有重叠部分各自保留即都绘制出来。假定源和目标的混合因子为:
Sr、Sg、Sb、Sa
Dr、Dg、Db、Da
其中Sr表示源的R分量的混合因子,Dr表示目标的R分量的混合因子,Sa表示源的alpha混合因子,Da表示目标的alpha混合因子,其它依次类似,相应地表示四元组RGBA的缩放因子。
一般地,源图像和目标图像重叠部分按照下述公式进行颜色的混合:
( Rs* Sr + Rd*Dr , Gs*Sg + Gd*Dg, Bs*Sb + Bd*Db, As*Sa + Ad*Da ) ----公式1
其中RGBA的下标s和d分别表示源的RGBA值和目标的RGBA值。将上述结果相乘相加后进行截取,以保证值在[0, 255]闭区间。如下代码:
#include <QtWidgets/QWidget>
#include "ui_compositionModeDemo.h"
#include<QPainter>
#include <QImage>
class compositionModeDemo : public QWidget
{
Q_OBJECT
public:
compositionModeDemo(QWidget *parent = Q_NULLPTR);
private :
void paintEvent(QPaintEvent *event)override;
private slots:
void srcAlphaValueChanged(int newAlphaValue);
void destAlphaValueChanged(int newAlphaValue);
private:
Ui::compositionModeDemoClass ui;
QImage* firstImage = nullptr;
QImage* secondImage = nullptr;
int m_srcAlpha{255};
int m_destAlpha{ 255 };
};
#include "compositionModeDemo.h"
compositionModeDemo::compositionModeDemo(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(ui.srcAlphaSlider, SIGNAL(valueChanged(int)), this, SLOT(srcAlphaValueChanged(int)));
connect(ui.destAlphaSlider, SIGNAL(valueChanged(int)), this, SLOT(destAlphaValueChanged(int)));
firstImage = new QImage(500, 500, QImage::Format_ARGB32_Premultiplied);
secondImage = new QImage(500, 500, QImage::Format_ARGB32_Premultiplied);
}
void compositionModeDemo::destAlphaValueChanged(int newAlphaValue)
{
m_destAlpha = newAlphaValue;
update();
}
void compositionModeDemo::srcAlphaValueChanged(int newAlphaValue)
{
m_srcAlpha = newAlphaValue;
update();
}
void compositionModeDemo::paintEvent(QPaintEvent *event)
{
//绘制第一张图片
QPainter painterFirst(firstImage);
firstImage->fill(Qt::transparent);//设置透明
painterFirst.setBrush(QColor(255, 0, 0, m_destAlpha));
painterFirst.drawRect(50, 50, 100, 100);
//绘制第二张图片
QPainter painterSecond(secondImage);
secondImage->fill(Qt::transparent);
painterSecond.setBrush(QColor(0, 0, 255, m_srcAlpha));
painterSecond.drawRect(100, 100, 100, 100);
//设置图片的重叠模式
painterFirst.setCompositionMode(QPainter::CompositionMode_SourceOver);
//重叠图片
painterFirst.drawImage(0, 0, *secondImage);
//展示图片
QPainter painter2(this);
painter2.drawImage(0, 0, *firstImage);
}
第一次绘制的红色矩形为目标,第二次绘制的蓝色矩形为源,采用的模式为QPainter::CompositionMode_SourceOver,则效果如下:
通过调节源alpha的滑块,改变其值,如下为源alpha为111时的效果:
如下为源apha为0即源完全透明的效果,即能透过源看到后面的目标:
当源的alpha为0时,公式1源和目标合成后的图像的alpha只有目标 Ad*Da 值了。然后保持源的alpha滑块在最大值255处不变,调节目标的alpha到中间,如下为目标的alpha为92的情况:
如下为目标的alpha为0的情况:
将上述cpp中第39行代码改为如下:
painterSecond.drawRect(200, 200, 100, 100);
以便目标和源不重叠,则输出如下,即源和目标都显示:
调节源和目标各自的alpha,都只对自己的透明度有影响。
可以看到:
-
源图像如果和目标图像有重叠,绘制时,则重叠部分是源图像在目标图像上面(源遮挡住目标),当源的alpha为255时,目标被源完全遮挡。
-
重叠部分RGBA值按公式1进行进行混合,混合之后得出的RGBA值为重叠部分的RGBA值。
-
没有重叠部分各自保留即都绘制出来。
2. QPainter::CompositionMode_DestinationOver
将上述cpp代码中的第42行改为QPainter::CompositionMode_DestinationOver,则: -
源图像如果和目标图像有重叠,绘制时,则重叠部分是目标图像在源图像上面(目标遮挡住源),当目标的alpha为255时,源被目标完全遮挡。。
-
重叠部分的源和重叠部分的目标按公式1进行进行混合,混合之后得出的RGBA值为重叠部分的RGBA值。
-
没有重叠部分各自保留即都绘制出来。
注意:和QPainter::CompositionMode_SourceOver的区别,唯一的区别是当源和目标有重叠时,目标盖住源,该模式是QPainter::CompositionMode_SourceOver的的逆操作。如下为源和目标的alpha都为255时的效果:
如下为源alpha为255,目标alpha为168效果:
如下为源alpha为255,目标alpha为0效果,即目标完全透明,能透过目标看到后面的源:
将上述cpp中第39行代码改为如下:
painterSecond.drawRect(200, 200, 100, 100);
以便目标和源不重叠,则输出如下,即源和目标都显示:
调节源和目标各自的alpha,都只对自己的透明度有影响。
3.
QPainter::CompositionMode_Source
将上面cpp代码的第42行改为QPainter::CompositionMode_Source,则:
- 无论什么情况,都只显示源,不显示目标,目标就像不存在、没绘制一样。如下为源和目标的alpha都为255的效果:
如下为源的alpha为0,目标alpha为255的效果:
可以看到,即使源完全透明,依然看不到目标。
4.QPainter::CompositionMode_Destination
将上面cpp代码第42行换为:QPainter::CompositionMode_Destination,则:
- 无论什么情况,都只显示目标,不显示源,源就像不存在、没绘制一样,该模式是QPainter::CompositionMode_Source逆操作。如下为源和目标的alpha都为255的效果:
如下为目标的alpha为0,源alpha为255的效果:
可以看到,即使目标完全透明,依然看不到源。
5.QPainter::CompositionMode_SourceIn
将上面cpp代码第42行换为:QPainter::CompositionMode_SourceIn,则:
- 如果目标和源有重叠,则只显示重叠部分中的源区域,不显示重叠部分的目标区域。目标和源不重叠部分都剔除、都不显示,
- 如果目标和源不重叠,则什么都不显示,不绘制,就像源和目标不存在一样。
- 目标和源重叠部分图像的alpha受到目标alpha的影响而削减(具体计算公式暂不清楚,知道的可以留言给我)。
如下为源和目标的alpha值都为255的结果:
如下为源的alpha值为255, 目标alpha值为0的结果:
如下为源的alpha值为0, 目标alpha值为255的结果:
如下为源的alpha值为0, 目标alpha值为0的结果:
如下为源的alpha值为128, 目标alpha值为133的结果:
将上述cpp中第39行代码改为如下:
painterSecond.drawRect(200, 200, 100, 100);
以便目标和源不重叠,则输出如下,即什么都不显示:
6.QPainter::CompositionMode_DestinationIn
将上面cpp代码第42行换为:QPainter::CompositionMode_DestinationIn,则:
- 只显示目标和源重叠部分中的目标部分,不显示重叠部分的源部分。目标和源不重叠部分都剔除、都不显示,
- 如果目标和源不重叠,则什么都不显示,不绘制,就像源和目标不存在一样。
- 目标和源重叠部分图像的alpha受到源的alpha的影响而削减(具体计算公式暂不清楚,知道的可以留言给我)。
- 该模式是QPainter::CompositionMode_SourceIn的逆操作。
如下为源和目标的alpha值都为255的结果:
如下为源的alpha值为255, 目标alpha值为0的结果:
如下为源的alpha值为0, 目标alpha值为255的结果:
如下为源的alpha值为0, 目标alpha值为0的结果:
如下为源的alpha值为128, 目标alpha值为116的结果:
将上述cpp中第39行代码改为如下:
painterSecond.drawRect(200, 200, 100, 100);
以便目标和源不重叠,则输出如下,即什么都不显示:
7.QPainter::CompositionMode_SourceOut
将上面cpp代码第42行换为:QPainter::CompositionMode_SourceOut,则:
- 如果源和目标有重叠,则输出全部的源,但源和目标重叠部分默认完全透明,属于目标但不属于目标和源重叠的部分不输出。此时目标alpha对重叠部分的alpha有影响即目标透明度影响重叠区域透明度;当目标alpha值为255即完全不透明时,源alpha对重叠区域alpha无影响,即此时源的透明度不影响重叠区域的透明度;当目标alpha值不为255即部分透明时,源alpha对重叠区域的alpha有影响,即此时源的透明度影响重叠区域透明度。
- 如果源和目标没有重叠,则输出全部源,不输出目标。此时目标透明度对源没有影响,这种情况下和QPainter::CompositionMode_Source相同。
如下为源和目标有重叠,且源alpha值为255, 目标alpha值为255的结果,可以看到重叠区域完全透明,
保持源alpha为255不变,分别调小目标的alpha为154、0,可以看到重叠区域受到目标透明度的影响:
保持目标alpha为255不变,分别调小源的alpha为103、38,可以看到重叠区域不受源透明度影响,此时源透明度只影响属于源自身但不属于重叠部分的源区域:
将目标alpha为135,源的alpha调为91、209,结果分别如下:
将上述cpp中第39行代码改为如下:
painterSecond.drawRect(200, 200, 100, 100);
即让源和目标不重叠,结果如下:
此时调节目标的alpha对源的透明度没任何影响。
8.QPainter::CompositionMode_DestinationOut
将上面cpp代码第42行换为:QPainter::CompositionMode_DestinationOut,则:
- 如果源和目标有重叠,则输出全部目标,但源和目标重叠部分默认完全透明,属于源但不属于源和目标重叠的部分不输出。此时源alpha对重叠部分的alpha有影响即源透明度影响重叠区域透明度;当源alpha值为255即完全不透明时,目标alpha对重叠区域alpha无影响,即此时目标的透明度不影响重叠区域的透明度;当源alpha值不为255即部分透明时,目标alpha对重叠区域的alpha有影响,即此时目标透明度影响重叠区域透明度。
- 如果源和目标没有重叠,则输出全部目标,不输出源。此时源透明度对目标没有影响,这种情况下和QPainter::CompositionMode_Destination相同。该模式是CompositionMode_SourceOut.模式的逆操作。
结果就是把CompositionMode_SourceOut.蓝色截图换成红色就行。
9.QPainter::CompositionMode_SourceAtop
将上面cpp代码第42行换为:QPainter::CompositionMode_SourceAtop,则:
- 输出全部目标。
- 如果源和目标有重叠,则只输出源重叠部分,且源重叠部分在目标的上面即遮挡住目标。源和目标不重叠的部分不输出。
- 如果源和目标没有重叠,则只输出全部的目标,不输出源。
如下为目标的alpha为255,源alpha为255时的效果::
重叠部分中,源在目标上面(遮挡住目标),重叠部分像素值由源和目标混合得出,源的alpha受到目标像素的alpha而削减。
如下为目标的alpha为0,源alpha为255时的效果:
如下为目标的alpha为255,源alpha为0时的效果:
如下为目标的alpha为128,源alpha为128时的效果:
将上述cpp代码中的39行改为如下,即源和目标不重叠
painterSecond.drawRect(200,200, 100, 100);
则效果如下:
可以看到只输出目标,没有输出源。
10.QPainter::CompositionMode_DestinationAtop
将上面cpp代码第42行换为:QPainter::CompositionMode_DestinationAtop,则:
- 输出全部源。
- 如果目标和源有重叠,则只输出目标重叠部分,且目标重叠部分在源上面即遮挡住源。目标和源不重叠的部分不输出。
- 如果目标和源没有重叠,则只输出全部的源,不输出目标。
- 该模式是QPainter::CompositionMode_SourceAtop的逆操作。
截图略。
11 QPainter::CompositionMode_Xor
源(其alpha值与目标alpha值的倒数成反比)与目标合并,该目标的alpha值与源alpha的倒数成反比。
截图略
12.QPainter::CompositionMode_Clear
源和目标无论在什么情况下都不显示,就像源、目标不存在一样。
总结:
- Qt总共有35种组合模式,但是很多不常用,所以本文就没再描述说明,当用户需要研究时,可以将上面cpp代码42行换成你要的模式,进行研究。
- 上面很多有关alpha减小、混合的内容,应该都有相应的公式的,但qt官方没有给出,这属于图形图像学的内容,OPengl中有一些,但和Qt的还是有些不同,如果读者知道这些混合方面的,alpha方面的公式,可以和我留言。
参考资料:
【1】:《 OpenGL编程指南(原书第7版)》 6.1节 混合。
【2】:QT多张图片的重叠显示