基本概念
- 目标(Destination):已经存在的像素。
- 源(Source):要绘制的新像素。
组合模式:决定源和目标如何混合。
总结
- SourceOver:源绘制在目标之上。
- DestinationOver:目标绘制在源之上。
- Clear:二者重叠区域被清空为透明。
- Source:结果完全由源决定。
- Destination:结果完全由目标决定。
- SourceIn:输出RGB值由源决定,且受到目标的透明度影响,只有在目标不透明处才显示。
- DestinationIn:输出RGB值由目标决定,且受到源的透明度影响。在源的透明区域,目标不保留。
- SourceOut:输出RGB值由源决定,且受到目标的不透明度的补集调制。结果在目标完全不透明的区域不显示。
- DestinationOut:输出RGB值由目标决定,且受到源的不透明度的补集调制。结果在源完全不透明的区域不显示
- SourceAtop:只在目标不透明的区域绘制,目标只在源透明的区域保留。
- DestinationAtop:只在源不透明的区域绘制,源只在目标透明的区域保留。
- Xor:源和目标在彼此透明的区域显示,在都不透明的区域变透明。
-
Plus:相当于 Photoshop 中的“线性减淡”混合模式。 - Multiply:相当于 Photoshop 中的“正片叠底”混合模式。
- Screen:相当于 Photoshop 中的“滤色”混合模式。
- Overlay:相当于 Photoshop 中的“叠加”混合模式。
- Darken:相当于 Photoshop 中的“变暗”混合模式。
- Lighten:相当于 Photoshop 中的“变亮”混合模式。
- ColorDodge:相当于 Photoshop 中的“颜色减淡”混合模式。
- ColorBurn:相当于 Photoshop 中的“颜色加深”混合模式。
- HardLight:相当于 Photoshop 中的“强光”混合模式。
- SoftLight:相当于 Photoshop 中的“柔光”混合模式。
- Difference:相当于 Photoshop 中的“差值”混合模式。
- Exclusion:相当于 Photoshop 中的“排除”混合模式。
-
SourceOrDestination:位运算,结果 = Source | Destination。 - SourceAndDestination:位运算,结果 = Source & Destination。
- SourceXorDestination:位运算,结果 = Source ^ Destination。
- NotSourceAndNotDestination:位运算,结果 = (~Source) & (~Destination)。
- NotSourceOrNotDestination:位运算,结果 = (~Source) | (~Destination)。
- NotSourceXorDestination:位运算,结果 = (~Source) ^ Destination。
- NotSource:位运算,结果 = ~Source。
- NotSourceAndDestination:位运算,结果 = (~Source) & Destination。
- SourceAndNotDestination:位运算,结果 = Source & (~Destination)。
- NotSourceOrDestination:位运算,结果 = (~Source) | Destination。
- SourceOrNotDestination:位运算,结果 = Source | (~Destination)。
- ClearDestination:无视目标像素和源像素,结果像素值所有位都设为 0。
- SetDestination:无视目标像素和源像素,结果像素值所有位都设为 1。
- NotDestination:位运算,结果 = ~Destination。
一、Porter-Duff合成模式
1~12为Porter-Duff合成模式。
计算时要预乘透明度。
下面这个模拟函数展示了这12种模式的原理:
QColor blendPorterDuff(const QColor &dst, const QColor &src, QPainter::CompositionMode mode)
{
// 获取预乘颜色值
qreal a_dst = dst.alphaF();
qreal a_src = src.alphaF();
qreal r_dst = dst.redF() * a_dst;
qreal g_dst = dst.greenF() * a_dst;
qreal b_dst = dst.blueF() * a_dst;
qreal r_src = src.redF() * a_src;
qreal g_src = src.greenF() * a_src;
qreal b_src = src.blueF() * a_src;
// 输出变量
qreal r_out = 0.0, g_out = 0.0, b_out = 0.0, a_out = 0.0;
// 根据模式选择合成算法
switch (mode) {
// Porter-Duff 12种基本模式
case QPainter::CompositionMode_Clear: // [0]
// R = 0
// A = 0
a_out = 0.0;
break;
case QPainter::CompositionMode_Source: // [1]
// R = Cs
// A = As
r_out = r_src;
g_out = g_src;
b_out = b_src;
a_out = a_src;
break;
case QPainter::CompositionMode_Destination: // [2]
// R = Cd
// A = Ad
r_out = r_dst;
g_out = g_dst;
b_out = b_dst;
a_out = a_dst;
break;
case QPainter::CompositionMode_SourceOver: // [3] (最常见的模式)
// R = Cs + Cd*(1-As)
// A = As + Ad*(1-As)
r_out = r_src + r_dst * (1.0 - a_src);
g_out = g_src + g_dst * (1.0 - a_src);
b_out = b_src + b_dst * (1.0 - a_src);
a_out = a_src + a_dst * (1.0 - a_src);
break;
case QPainter::CompositionMode_DestinationOver: // [4]
// R = Cd + Cs*(1-Ad)
// A = Ad + As*(1-Ad)
r_out = r_dst + r_src * (1.0 - a_dst);
g_out = g_dst + g_src * (1.0 - a_dst);
b_out = b_dst + b_src * (1.0 - a_dst);
a_out = a_dst + a_src * (1.0 - a_dst);
break;
case QPainter::CompositionMode_SourceIn: // [5]
// R = Cs*Ad
// A = As*Ad
r_out = r_src * a_dst;
g_out = g_src * a_dst;
b_out = b_src * a_dst;
a_out = a_src * a_dst;
break;
case QPainter::CompositionMode_DestinationIn: // [6]
// R = Cd*As
// A = Ad*As
r_out = r_dst * a_src;
g_out = g_dst * a_src;
b_out = b_dst * a_src;
a_out = a_dst * a_src;
break;
case QPainter::CompositionMode_SourceOut: // [7]
// R = Cs*(1-Ad)
// A = As*(1-Ad)
r_out = r_src * (1.0 - a_dst);
g_out = g_src * (1.0 - a_dst);
b_out = b_src * (1.0 - a_dst);
a_out = a_src * (1.0 - a_dst);
break;
case QPainter::CompositionMode_DestinationOut: // [8]
// R = Cd*(1-As)
// A = Ad*(1-As)
r_out = r_dst * (1.0 - a_src);
g_out = g_dst * (1.0 - a_src);
b_out = b_dst * (1.0 - a_src);
a_out = a_dst * (1.0 - a_src);
break;
case QPainter::CompositionMode_SourceAtop: // [9]
// R = Cs*Ad + Cd*(1-As)
// A = As*Ad + Ad*(1-As) = Ad
r_out = r_src * a_dst + r_dst * (1.0 - a_src);
g_out = g_src * a_dst + g_dst * (1.0 - a_src);
b_out = b_src * a_dst + b_dst * (1.0 - a_src);
a_out = a_dst; // 简化计算
break;
case QPainter::CompositionMode_DestinationAtop: // [10]
// R = Cd*As + Cs*(1-Ad)
// A = Ad*As + As*(1-Ad) = As
r_out = r_dst * a_src + r_src * (1.0 - a_dst);
g_out = g_dst * a_src + g_src * (1.0 - a_dst);
b_out = b_dst * a_src + b_src * (1.0 - a_dst);
a_out = a_src; // 简化计算
break;
case QPainter::CompositionMode_Xor: // [11]
// R = Cs*(1-Ad) + Cd*(1-As)
// A = As*(1-Ad) + Ad*(1-As)
r_out = r_src * (1.0 - a_dst) + r_dst * (1.0 - a_src);
g_out = g_src * (1.0 - a_dst) + g_dst * (1.0 - a_src);
b_out = b_src * (1.0 - a_dst) + b_dst * (1.0 - a_src);
a_out = a_src * (1.0 - a_dst) + a_dst * (1.0 - a_src);
break;
default:
// 默认使用 SourceOver
r_out = r_src + r_dst * (1.0 - a_src);
g_out = g_src + g_dst * (1.0 - a_src);
b_out = b_src + b_dst * (1.0 - a_src);
a_out = a_src + a_dst * (1.0 - a_src);
break;
}
// 处理完全透明的情况(避免除以零)
if (a_out <= 0.0) {
return QColor(0, 0, 0, 0);
}
// 转换回非预乘格式并裁剪范围
r_out = qBound(0.0, r_out / a_out, 1.0);
g_out = qBound(0.0, g_out / a_out, 1.0);
b_out = qBound(0.0, b_out / a_out, 1.0);
a_out = qBound(0.0, a_out, 1.0);
return QColor::fromRgbF(r_out, g_out, b_out, a_out);
}
1、SourceOver
默认模式,源覆盖在目标上。
2、DestinationOver
源被放置在目标的下面。这就是说要看见新绘制的内容,已有的内容不能是不透明的。
3、Clear
将目标和源重叠的部分设置为透明。使用这个模式进行绘制时,相当于“橡皮擦”效果。
QImage image(200, 200, QImage::Format_ARGB32);
image.fill(Qt::blue); // 背景是蓝色的
QPainter painter(&image);
painter.setCompositionMode(QPainter::CompositionMode_Clear);
painter.fillRect(50, 50, 100, 100, Qt::white); // 这个区域会被清空(变成透明或默认背景)
painter.end();
image.save("D://123.png");
4、Source
输出结果完全由源像素决定,目标像素会被直接忽略(无论源像素是否透明)。
5、Destination
与 Source 模式相反,输出结果完全由目标像素决定,源像素会被完全忽略。也就是新绘制的内容不会产生影响。
void drawImage()
{
QColor destinationColor = QColor(255,0,0,100);
QColor sourceColor = QColor(0,255,0,100);
QImage image(500, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
painter.setBrush(destinationColor);
painter.drawRect(210, 10, 100, 100);
painter.setCompositionMode(QPainter::CompositionMode_Destination);
painter.setBrush(sourceColor);
painter.drawRect(250, 50, 100, 100);
image.save("D://123.png");
}
如图,源像素没有绘制出来。
这种模式看起来没用,实际上可以用来根据某种标识选择性绘制。
void ColorBlenderWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.fillRect(rect(), QColor(240, 240, 240));
painter.setPen(Qt::darkGray);
painter.drawRect(rect().adjusted(0, 0, -1, -1));
// 根据条件绘制或不绘制logo
QRect logoRect(20, 20, 100, 50);
bool shouldDrawLogo = false;
{
painter.save();
if(!shouldDrawLogo)
{
painter.setCompositionMode(QPainter::CompositionMode_Destination);
}
// 正常绘制logo
painter.fillRect(logoRect, QColor(200, 50, 50));
painter.setPen(Qt::white);
painter.drawText(logoRect, Qt::AlignCenter, "LOGO");
painter.restore();
}
// 绘制其他内容
painter.setPen(Qt::black);
painter.drawText(QRect(20, 80, 200, 30),
QString("Logo显示状态: %1").arg(shouldDrawLogo ? "开" : "关"));
}
使用此模式通过设置一个标识可以对绘制内容进行控制。
6、SourceIn
此模式可以结合上面的模拟函数理解:r_out = r_src * a_dst; g_out = g_src * a_dst; b_out = b_src * a_dst; a_out = a_src * a_dst;
可见此模式的特点:
- 结果的RGB值由源决定,且受到目标的透明度影响。
- 只有在目标不透明处才显示(在目标透明处 a_out = a_src * a_dst 结果是 0)
void drawImage()
{
QImage image(500, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QLinearGradient gradient(0, 0, 400, 0);
gradient.setColorAt(0, QColor(0, 0, 0, 0)); // 左:完全透明(Alpha=0)
gradient.setColorAt(1, QColor(0, 0, 0, 255)); // 右:完全不透明(Alpha=255)
painter.fillRect(QRect(0, 0, 400, 200), gradient);
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
painter.fillRect(QRect(0, 0, 400, 200), Qt::red);
image.save("D://123.png");
}
如图,填充一个不透明的矩形,视觉效果是渐变矩形。
ColorBlenderWidget::ColorBlenderWidget(QWidget *parent)
: QWidget(parent)
{
setAttribute(Qt::WA_OpaquePaintEvent);//窗口背景透明
}
void ColorBlenderWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QFont font("Arial", 48, QFont::Bold);
painter.setFont(font);
QPainterPath textPath;
textPath.addText(20, height()/2 + 20, font, "黄河之水天上来");
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::black);
painter.drawPath(textPath);
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);//注意背景透明
QLinearGradient grad(0, 0, width(), 0);
grad.setColorAt(0, Qt::red);
grad.setColorAt(0.5, Qt::yellow);
grad.setColorAt(1, Qt::green);
painter.fillRect(rect(), grad);
}
如图,透明背景上先绘制黑色文字,再在整个窗口填充渐变,视觉效果是渐变只显示在非透明区(黑色文字上)。
7、DestinationIn
此模式可结合上面的模拟函数来看:
r_out = r_dst * a_src; g_out = g_dst * a_src; b_out = b_dst * a_src; a_out = a_dst * a_src;
可见:
- 结果的RGB值由目标决定,且受到源的透明度影响。
- 在源的透明区域,目标不保留(源透明的区域 a_out = a_dst * a_src 结果是 0)。
void drawImage()
{
QImage image(500, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QLinearGradient gradient(0, 0, 400, 0);
gradient.setColorAt(0, QColor(0, 0, 0, 0)); // 左:完全透明(Alpha=0)
gradient.setColorAt(1, QColor(0, 0, 0, 255)); // 右:完全不透明(Alpha=255)
painter.fillRect(QRect(0, 0, 400, 200), gradient);
painter.fillRect(QRect(0, 260, 400, 200), gradient);
painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
QColor color = Qt::red;
color.setAlpha(120);
painter.fillRect(QRect(0, 0, 400, 200), color);
image.save("D://123.png");
}
如图,先绘制两个相同的渐变矩形,下方的渐变矩形作为对比,上方的渐变矩形作为目标,然后在上方渐变矩形上使用此模式绘制半透明红色,可见:绘制的不透明红色(源)的RGB没有起作用,源的透明度稀释了目标颜色。
8、SourceOut
此模式可结合上面的模拟函数来看:r_out = r_src * (1.0 - a_dst); g_out = g_src * (1.0 - a_dst); b_out = b_src * (1.0 - a_dst); a_out = a_src * (1.0 - a_dst);
可见:
- 结果的RGB值由源决定,且受到目标的不透明度的补集调制。
- 结果在目标完全不透明的区域不显示(目标完全不透明时 a_out = a_src * (1.0 - a_dst) 结果为 0)
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
// 1. 绘制目标图像(Destination):一个从左到右 alpha 渐变(0.0 -> 1.0)的蓝色矩形
painter.fillRect(0, 0, 200, 200, QColor(0, 0, 255, 0)); // 完全透明(alpha=0)
painter.fillRect(200, 0, 200, 200, QColor(0, 0, 255, 128)); // 半透明(alpha=128)
painter.fillRect(400, 0, 200, 200, QColor(0, 0, 255, 255)); // 完全不透明(alpha=255)
// 2. 设置合成模式为 SourceOut
painter.setCompositionMode(QPainter::CompositionMode_SourceOut);
// 3. 绘制源图像(Source):一个完全不透明(alpha=255)的红色矩形
painter.fillRect(50, 50, 600, 100, Qt::red);
painter.end();
image.save("D://123.png");
}
如图,三种不同透明度的颜色作为目标,可见:
- 如果目标完全不透明(alpha = 1.0),则源完全消失(因为 1 - 1 = 0)。
- 如果目标完全透明(alpha = 0.0),则源完全保留(因为 1 - 0 = 1)。
- 中间值会按比例缩减源的 alpha。
9、DestinationOut
此模式可结合上面的模拟函数来看:r_out = r_dst * (1.0 - a_src); g_out = g_dst * (1.0 - a_src); b_out = b_dst * (1.0 - a_src); a_out = a_dst * (1.0 - a_src);
可见:
- 结果的RGB值由目标决定,且受到源的不透明度的补集调制。
- 结果在源完全不透明的区域不显示(源完全不透明时 a_out = a_dst * (1.0 - a_src) 结果为 0)
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
painter.fillRect(50, 50, 600, 100, Qt::red);
painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
painter.fillRect(0, 0, 200, 200, QColor(0, 0, 255, 0)); // 完全透明蓝色(alpha=0)
painter.fillRect(200, 0, 200, 200, QColor(0, 0, 255, 128)); // 半透明蓝色(alpha=128)
painter.fillRect(400, 0, 200, 200, QColor(0, 0, 255, 255)); // 完全不透明蓝色(alpha=255)
image.save("D://123.png");
}
如图,三种不同透明度的源覆盖目标。
10、SourceAtop
此模式可结合上面的模拟函数来看:
r_out = r_src * a_dst + r_dst * (1.0 - a_src); g_out = g_src * a_dst + g_dst * (1.0 - a_src); b_out = b_src * a_dst + b_dst * (1.0 - a_src); a_out = a_dst;
可见:
- 结果只在目标不透明的区域绘制(a_out = a_dst)。
- 目标只在源透明的区域保留(在源不透明时 out = src * a_dst + dst * (1.0 - a_src) 的结果为:out = src * a_dst,没有目标的颜色值参与)。
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
// 1. 绘制目标图像(Destination):三个不同透明度的蓝色矩形
painter.fillRect(0, 0, 200, 200, QColor(0, 0, 255, 255)); // 完全不透明
painter.fillRect(200, 0, 200, 200, QColor(0, 0, 255, 128)); // 半透明
painter.fillRect(400, 0, 200, 200, QColor(0, 0, 255, 0)); // 完全透明
// 2. 设置合成模式为 SourceAtop
painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
// 3. 绘制源图像(Source):一个完全不透明的红色矩形(覆盖所有三个区域)
painter.fillRect(50, 50, 500, 100, Qt::red);
image.save("D://123.png");
}
如图,三种不同透明度的蓝色作为目标,不透明的红色覆盖它们。
11、DestinationAtop
此模式可结合上面的模拟函数来看:
r_out = r_dst * a_src + r_src * (1.0 - a_dst); g_out = g_dst * a_src + g_src * (1.0 - a_dst); b_out = b_dst * a_src + b_src * (1.0 - a_dst); a_out = a_src;
可见:
- 结果只在源不透明的区域绘制(a_out = a_src)。
- 源只在目标透明的区域保留(在目标不透明时 out = dst * a_src + src * (1.0 - a_dst) 的结果为:out = dst * a_src,没有源的颜色值参与)。
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
painter.fillRect(0, 0, 200, 200, QColor(0, 0, 255, 255)); // 完全不透明
painter.fillRect(200, 0, 200, 200, QColor(0, 0, 255, 128)); // 半透明
painter.fillRect(400, 0, 200, 200, QColor(0, 0, 255, 0)); // 完全透明
painter.setCompositionMode(QPainter::CompositionMode_DestinationAtop);
QColor color = Qt::red;
color.setAlpha(100);
painter.fillRect(0, 50, 700, 100, color);
image.save("D://123.png");
}
如图,三种不同透明度的蓝色作为目标,半透明红色作为源。
12、Xor
此模式可结合上面的模拟函数来看:
r_out = r_src * (1.0 - a_dst) + r_dst * (1.0 - a_src); g_out = g_src * (1.0 - a_dst) + g_dst * (1.0 - a_src); b_out = b_src * (1.0 - a_dst) + b_dst * (1.0 - a_src); a_out = a_src * (1.0 - a_dst) + a_dst * (1.0 - a_src);
可见,此模式的核心特点是:源和目标在彼此透明的区域显示,在都不透明的区域变透明。
更具体地说:
- 在源不透明、目标透明的区域:显示源。
- 在源透明、目标不透明的区域:显示目标。
- 在源和目标都不透明的区域:两者都消失(变透明)。
void drawImage()
{
QImage image(500, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
QColor source(QColor(255,0,0));
QColor dest(QColor(0,255,0));
// 1、使用 Qt 的 XOR 合成模式绘制红矩形和蓝圆重叠区域
painter.setBrush(source);
painter.drawRect(50, 50, 100, 100); // 红色矩形
painter.setCompositionMode(QPainter::CompositionMode_Xor);
painter.setBrush(dest);
painter.drawEllipse(100, 100, 100, 100); // 蓝色圆形
image.save("D://123.png");
}
二、类似 Photoshop 中的混合模式
下面的13~24的原理在上图可见。
注:这些模式的数学计算公式仅依赖 RGB 值,不直接处理 Alpha,下面的例子也不处理 Alpha。
13、Plus
源颜色和目标颜色的 RGB 分量逐通道加。类似于 Photoshop 中的“线性减淡”混合模式。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255); //不透明
}
QColor simulatePlusMode(const QColor &src, const QColor &dst)
{
int srcR = src.red();
int srcG = src.green();
int srcB = src.blue();
int dstR = dst.red();
int dstG = dst.green();
int dstB = dst.blue();
return QColor::fromRgb(qBound(0,srcR + dstR,255),
qBound(0,srcG + dstG,255),
qBound(0,srcB + dstB,255),
255);
}
void drawImage()
{
QPixmap pixmap(900, 300);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
QColor color1 = randomColor();
QColor color2 = randomColor();
painter.setBrush(color1);
painter.drawRect(50, 50, 100, 100);
painter.setCompositionMode(QPainter::CompositionMode_Plus);
painter.setBrush(color2);
painter.drawRect(100, 75, 100, 100);
QColor color3 = simulatePlusMode(color1,color2);
painter.setBrush(color3);
painter.drawRect(300, 75, 100, 100);
pixmap.save("D://123.png");
}
这个模拟函数说明了该模式的工作方式。
14、Multiply
将源颜色和目标颜色的 RGB 分量逐通道相乘。类似于 Photoshop 中的“正片叠底”混合模式。
白色作为源不会改变目标颜色。
黑色作为源会吸收所有颜色,结果总是黑色。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor multiplyColors(const QColor &src, const QColor &dst)
{
int srcR = src.red();
int srcG = src.green();
int srcB = src.blue();
int dstR = dst.red();
int dstG = dst.green();
int dstB = dst.blue();
return QColor::fromRgb(qBound(0,srcR * dstR / 255,255),
qBound(0,srcG * dstG / 255,255),
qBound(0,srcB * dstB / 255,255),
255);
}
void drawImage()
{
QPixmap pixmap(900, 300);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
QColor color1 = randomColor();
QColor color2 = randomColor();
painter.setBrush(color1);
painter.drawRect(50, 50, 100, 100);
painter.setCompositionMode(QPainter::CompositionMode_Multiply);
painter.setBrush(color2);
painter.drawRect(100, 75, 100, 100);
QColor color3 = multiplyColors(color1,color2);
painter.setBrush(color3);
painter.drawRect(300, 75, 100, 100);
pixmap.save("D://123.png");
}
这个模拟函数展示了此模式的工作原理。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor multiplyColors(const QColor &src, const QColor &dst)
{
int srcR = src.red();
int srcG = src.green();
int srcB = src.blue();
int dstR = dst.red();
int dstG = dst.green();
int dstB = dst.blue();
return QColor::fromRgb(qBound(0,srcR * dstR / 255,255),
qBound(0,srcG * dstG / 255,255),
qBound(0,srcB * dstB / 255,255),
255);
}
void demonstrateMultiplyFeatures()
{
QImage image(500, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
// 1. 白色是乘法单位元(不改变目标颜色)
{
painter.setPen(Qt::black);
painter.setBrush(QColor(100, 150, 200)); // 初始蓝色
painter.drawRect(50, 50, 200, 100);
painter.setCompositionMode(QPainter::CompositionMode_Multiply);
painter.setBrush(Qt::white); // 白色
painter.drawRect(100, 70, 200, 100); // 叠加后蓝色不变
painter.setCompositionMode(QPainter::CompositionMode_SourceOver); // 重置混合模式
painter.setBrush(multiplyColors(Qt::white,QColor(100, 150, 200)));
painter.drawRect(350, 70, 50, 100);
}
// 2. 黑色会吸收所有颜色(结果黑色)
{
painter.setBrush(QColor(255, 200, 0)); // 初始黄色
painter.drawRect(50, 250, 200, 100);
painter.setCompositionMode(QPainter::CompositionMode_Multiply);
painter.setBrush(Qt::black);
painter.drawRect(100, 270, 200, 100); // 叠加区域变为黑色
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.setBrush(multiplyColors(Qt::black,QColor(255, 200, 0)));
painter.drawRect(350, 270, 50, 100);
}
image.save("D://789.png");
}
15、Screen
反色相乘后再反色,使颜色变亮,与白色混合得白色,与黑色混合则保留原色。类似于 Photoshop 中的“滤色”混合模式。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor screenBlendColors(const QColor &dst, const QColor &src)
{
auto screenBlend = [](int dst,int src)
{
return 255 - ((255 - dst) * (255 - src)) / 255;
};
return QColor::fromRgb(qBound(0,screenBlend(dst.red(),src.red()),255),
qBound(0,screenBlend(dst.green(),src.green()),255),
qBound(0,screenBlend(dst.blue(),src.blue()),255),
255);
}
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QColor dst = randomColor();
painter.fillRect(0, 0, 200, 200, dst);
painter.setCompositionMode(QPainter::CompositionMode_Screen);
QColor src = randomColor();
painter.fillRect(100, 100, 200, 200, src);
QColor mixedColor = screenBlendColors(dst, src);
// 绘制第三个矩形,使用混合后的颜色
painter.fillRect(350, 100, 200, 200, mixedColor);
image.save("D://123.png");
}
这个模拟函数展示了此模式的工作方式。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor screenBlendColors(const QColor &dst, const QColor &src)
{
auto screenBlend = [](int dst,int src)
{
return 255 - ((255 - dst) * (255 - src)) / 255;
};
return QColor::fromRgb(qBound(0,screenBlend(dst.red(),src.red()),255),
qBound(0,screenBlend(dst.green(),src.green()),255),
qBound(0,screenBlend(dst.blue(),src.blue()),255),
255);
}
void demonstrateScreenMode()
{
// 创建透明画布
QImage canvas(700, 600, QImage::Format_ARGB32);
canvas.fill(Qt::transparent);
QPainter painter(&canvas);
painter.setRenderHint(QPainter::Antialiasing);
// 1. 基本公式演示 -----------------------------------
QColor baseColor(153, 0, 0);
QColor blendColor(0, 0, 77);
painter.setPen(Qt::black);
painter.drawText(50, 30, "1. 预期");
painter.fillRect(50, 50, 100, 100, baseColor);
painter.drawText(50, 170, "基色");
painter.fillRect(200, 50, 100, 100, blendColor);
painter.drawText(200, 170, "混合色");
painter.fillRect(350, 50, 100, 100, baseColor);
painter.setCompositionMode(QPainter::CompositionMode_Screen);
painter.fillRect(350, 50, 100, 100, blendColor);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawText(350, 170, "结果");
painter.fillRect(500, 50, 100, 100, screenBlendColors(baseColor, blendColor));
painter.drawText(500, 170, "预期值");
// 2. 与白色混合得白色 -------------------------------
painter.drawText(50, 220, "2. 与白色混合 → 白色");
QColor anyColor = randomColor();
painter.fillRect(50, 240, 100, 100, anyColor);
painter.drawText(50, 360, "任意颜色");
painter.fillRect(200, 240, 100, 100, anyColor);
painter.setCompositionMode(QPainter::CompositionMode_Screen);
painter.fillRect(200, 240, 100, 100, Qt::white);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawText(200, 360, "结果应为白色");
// 3. 与黑色混合 透明--------------------------------
painter.drawText(350, 220, "3. 与黑色混合 → 结果不变");
painter.fillRect(350, 240, 100, 100, anyColor);
painter.drawText(350, 360, "相同任意颜色");
painter.fillRect(500, 240, 100, 100, anyColor);
painter.setCompositionMode(QPainter::CompositionMode_Screen);
painter.fillRect(500, 240, 100, 100, Qt::black);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawText(500, 360, "结果不变");
// 4. 可交换性演示 ---------------------------------
painter.drawText(50, 410, "4. 可交换性: Screen(A,B) ≡ Screen(B,A)");
QColor colorA = randomColor();
QColor colorB = randomColor();
painter.fillRect(50, 430, 100, 100, colorA);
painter.setCompositionMode(QPainter::CompositionMode_Screen);
painter.fillRect(50, 430, 100, 100, colorB);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.fillRect(200, 430, 100, 100, colorB);
painter.setCompositionMode(QPainter::CompositionMode_Screen);
painter.fillRect(200, 430, 100, 100, colorA);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawText(50, 550, "A+B");
painter.drawText(200, 550, "B+A");
canvas.save("D://789.png");
}
这个例子展示了此模式的一些特性。
16、Overlay
基于目标颜色的通道亮度,决定是进行“正片叠底”还是“滤色”的混合:
- 目标颜色亮度 < 0.5,则变暗(正片叠底)。
- 目标颜色亮度 ≥ 0.5,则提亮(滤色)。
目标亮则结果更亮,目标暗则结果更暗。类似于 Photoshop 中的“叠加”混合模式。
与 HardLight 模式类似,但此模式以目标颜色为基准,而 HardLight 以源颜色为基准。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor overlayBlendColors(const QColor &dst, const QColor &src)
{
auto overlayBlendChannel = [](float src, float dst) -> int {
src /= 255.0f; // 归一化到 [0, 1]
dst /= 255.0f;
float result;
if (dst < 0.5f)
{
result = 2 * src * dst; // 正片叠底(Multiply)
} else {
result = 1 - 2 * (1 - src) * (1 - dst); // 滤色(Screen)
}
return static_cast<int>(qBound(0.0f, result, 1.0f) * 255.0f); // 还原到 [0, 255]
};
int r = overlayBlendChannel(static_cast<float>(src.red()), static_cast<float>(dst.red()));
int g = overlayBlendChannel(static_cast<float>(src.green()), static_cast<float>(dst.green()));
int b = overlayBlendChannel(static_cast<float>(src.blue()), static_cast<float>(dst.blue()));
return QColor(r, g, b, 255);
}
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QColor dst = randomColor();
painter.fillRect(0, 0, 200, 200, dst);
painter.setCompositionMode(QPainter::CompositionMode_Overlay);
QColor src = randomColor();
painter.fillRect(100, 100, 200, 200, src);
QColor mixedColor = overlayBlendColors(dst, src);
// 绘制第三个矩形,使用混合后的颜色
painter.fillRect(350, 100, 200, 200, mixedColor);
image.save("D://123.png");
}
这个模拟函数说明了此模式的工作原理。
17、Darken
比较源颜色和目标颜色的每个颜色通道,并选择更暗的值作为最终绘制的颜色。类似于 Photoshop 中的“变暗”混合模式。
下面这个例子绘制三个矩形,比背景亮的没有显示:
QImage image(400, 400, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
// 先画一个浅色背景(比如浅黄色)
painter.fillRect(0, 0, 400, 400, QColor(255, 250, 200));
// 设置合成模式为 Darken
painter.setCompositionMode(QPainter::CompositionMode_Darken);
// 在图像上绘制几个不同颜色的图形
painter.setBrush(QColor(200, 100, 100)); // 红色
painter.drawRect(50, 50, 100, 100); // 红色矩形
painter.setBrush(QColor(100, 200, 100)); // 绿色
painter.drawRect(100, 100, 100, 100); // 绿色矩形
painter.setBrush(QColor(Qt::white));
painter.drawRect(150, 150, 100, 100);
image.save("D://123.png");
18、Lighten
比较源颜色和目标颜色的每个颜色通道,并选择更亮的值作为最终绘制的颜色。类似于 Photoshop 中的“变亮”混合模式。
19、ColorDodge
提亮目标颜色(降低目标颜色的暗度)来反映源颜色,黑色源色不会对目标产生影响。类似于 Photoshop 中的“颜色减淡”混合模式。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor colorDodgeBlendColors(const QColor &dst, const QColor &src)
{
auto colorDodgeBlend = [](int dst,int src)
{
return dst + (dst * src) / (255 - src);
};
return QColor::fromRgb(qBound(0,colorDodgeBlend(dst.red(),src.red()),255),
qBound(0,colorDodgeBlend(dst.green(),src.green()),255),
qBound(0,colorDodgeBlend(dst.blue(),src.blue()),255),
255);
}
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QColor dst = randomColor();
painter.fillRect(0, 0, 200, 200, dst);
painter.setCompositionMode(QPainter::CompositionMode_ColorDodge);
QColor src = randomColor();
painter.fillRect(100, 100, 200, 200, src);
QColor mixedColor = colorDodgeBlendColors(dst, src);
// 绘制第三个矩形,使用混合后的颜色
painter.fillRect(350, 100, 200, 200, mixedColor);
image.save("D://123.png");
}
这个模拟函数展示了此模式的原理。
20、ColorBurn
降低目标颜色的亮度来增强对比度,尤其强化暗部区域。类似于 Photoshop 中的“颜色加深”混合模式。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor colorBurnBlendColors(const QColor &dst, const QColor &src)
{
auto colorBurnBlend = [](int dst,int src)
{
return dst - ((255 - dst) * (255 - src)) / src;
};
return QColor::fromRgb(qBound(0,colorBurnBlend(dst.red(),src.red()),255),
qBound(0,colorBurnBlend(dst.green(),src.green()),255),
qBound(0,colorBurnBlend(dst.blue(),src.blue()),255),
255);
}
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QColor dst = randomColor();
painter.fillRect(0, 0, 200, 200, dst);
painter.setCompositionMode(QPainter::CompositionMode_ColorBurn);
QColor src = randomColor();
painter.fillRect(100, 100, 200, 200, src);
QColor mixedColor = colorBurnBlendColors(dst, src);
// 绘制第三个矩形,使用混合后的颜色
painter.fillRect(350, 100, 200, 200, mixedColor);
image.save("D://123.png");
}
这个模拟函数展示了此模式的原理。
21、HardLight
基于源颜色的通道亮度,决定是进行“正片叠底”还是“滤色”的混合:
- 源颜色亮度 < 0.5,则变暗(正片叠底)。
- 源颜色亮度 ≥ 0.5,则提亮(滤色)。
源亮则结果更亮,源暗则结果更暗。类似于 Photoshop 中的“强光”混合模式。
与 Overlay 模式类似,但此模式以源颜色为基准,而 Overlay 以目标颜色为基准。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor hardLightBlendColors(const QColor &dst, const QColor &src)
{
auto hardLightBlendChannel = [](float src, float dst) -> int {
src /= 255.0f; // 归一化到 [0, 1]
dst /= 255.0f;
float result;
if (src <= 0.5f) {
result = 2 * src * dst; // 正片叠底(Multiply)
} else {
result = 1 - 2 * (1 - src) * (1 - dst); // 滤色(Screen)
}
return static_cast<int>(qBound(0.0f, result, 1.0f) * 255.0f); // 还原到 [0, 255]
};
int r = hardLightBlendChannel(static_cast<float>(src.red()), static_cast<float>(dst.red()));
int g = hardLightBlendChannel(static_cast<float>(src.green()), static_cast<float>(dst.green()));
int b = hardLightBlendChannel(static_cast<float>(src.blue()), static_cast<float>(dst.blue()));
return QColor(r, g, b, 255);
}
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QColor dst = randomColor();
painter.fillRect(0, 0, 200, 200, dst);
painter.setCompositionMode(QPainter::CompositionMode_HardLight);
QColor src = randomColor();
painter.fillRect(100, 100, 200, 200, src);
QColor mixedColor = hardLightBlendColors(dst, src);
// 绘制第三个矩形,使用混合后的颜色
painter.fillRect(350, 100, 200, 200, mixedColor);
image.save("D://123.png");
}
这个模拟函数展示了此模式的工作原理。
22、SoftLight
比 HardLight 更柔和,类似漫反射光的效果,适合自然的光影调整。
基于源颜色的通道亮度动态调整目标颜色的对比度:
- 源颜色亮度 > 0.5:轻微提亮目标颜色。
- 源颜色亮度 ≤ 0.5:轻微压暗目标颜色。
类似于 Photoshop 中的“柔光”混合模式。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor softLightBlendColors(const QColor &dst, const QColor &src)
{
auto blendChannel = [](float src, float dst) -> float {
src /= 255.0f; // 归一化到 [0, 1]
dst /= 255.0f;
float result;
if (src > 0.5f) {
// 提亮公式
result = dst + (2.0f * src - 1.0f) * (sqrt(dst) - dst);
} else {
// 压暗公式
result = dst - (1.0f - 2.0f * src) * dst * (1.0f - dst);
}
return qBound(0.0f, result, 1.0f) * 255.0f; // 还原到 [0, 255]
};
int r = blendChannel(src.red(), dst.red());
int g = blendChannel(src.green(), dst.green());
int b = blendChannel(src.blue(), dst.blue());
return QColor(r, g, b, 255);
}
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QColor dst = randomColor();
painter.fillRect(0, 0, 200, 200, dst);
painter.setCompositionMode(QPainter::CompositionMode_SoftLight);
QColor src = randomColor();
painter.fillRect(100, 100, 200, 200, src);
QColor mixedColor = softLightBlendColors(dst, src);
// 绘制第三个矩形,使用混合后的颜色
painter.fillRect(350, 100, 200, 200, mixedColor);
image.save("D://123.png");
}
这个模拟函数展示了此模式的原理。
23、Difference
目标和源的颜色二者中较亮的颜色中减去较暗的颜色。类似于 Photoshop 中的“差值”混合模式。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor differenceBlend(const QColor &dst, const QColor &src)
{
int r = std::abs(dst.red() - src.red());
int g = std::abs(dst.green() - src.green());
int b = std::abs(dst.blue() - src.blue());
return QColor(r, g, b, 255); // Alpha 固定为不透明
}
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QColor dst = randomColor();
painter.fillRect(0, 0, 200, 200, dst);
painter.setCompositionMode(QPainter::CompositionMode_Difference);
QColor src = randomColor();
painter.fillRect(100, 100, 200, 200, src);
QColor mixedColor = differenceBlend(dst, src);
// 绘制第三个矩形,使用混合后的颜色
painter.fillRect(350, 100, 200, 200, mixedColor);
image.save("D://123.png");
}
这个函数展示了此模式的原理。
24、Exclusion
类似于 Difference 模式,但整体效果要柔和。类似于 Photoshop 中的“排除”混合模式。
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor exclusionBlend(const QColor &dst, const QColor &src)
{
auto blendChannel = [](int dstChannel, int srcChannel) -> int {
return dstChannel + srcChannel - 2 * dstChannel * srcChannel / 255;
};
int r = blendChannel(dst.red(), src.red());
int g = blendChannel(dst.green(), src.green());
int b = blendChannel(dst.blue(), src.blue());
return QColor(r, g, b, 255); // Alpha 固定为不透明
}
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QColor dst = randomColor();
painter.fillRect(0, 0, 200, 200, dst);
painter.setCompositionMode(QPainter::CompositionMode_Exclusion);
QColor src = randomColor();
painter.fillRect(100, 100, 200, 200, src);
QColor mixedColor = exclusionBlend(dst, src);
// 绘制第三个矩形,使用混合后的颜色
painter.fillRect(350, 100, 200, 200, mixedColor);
image.save("D://123.png");
}
这个模拟函数展示了此模式的原理。
三、位运算混合模式
剩余的模式都是基于位运算来实现像素的混合。
位运算混合在特定的历史条件下非常适合当时的硬件和软件环境。然而,随着技术的进步,尤其是对更高图像质量和更复杂视觉效果的需求,这些传统方法逐渐被更为先进的合成技术和算法所取代。尽管如此,在某些特定的情况下,比如低级图形编程或特定硬件加速中,位运算仍然可能找到它的用武之地。
25、SourceOrDestination
将源和目标像素进行按位或(OR)操作。
原理:
QColor sourceOrDestination(const QColor& src, const QColor& dst)
{
// 确保颜色都是预乘格式
QColor source = src.toRgb();
QColor destination = dst.toRgb();
// 对每个通道执行按位OR操作
int r = source.red() | destination.red();
int g = source.green() | destination.green();
int b = source.blue() | destination.blue();
// 返回结果颜色,保持原始alpha预乘状态
return QColor(r, g, b);
}
这个模式可以用来叠加图像,从二进制数据来看:
源像素(红色):
- R: 11111111 (255)
- G: 00000000 (0)
- B: 00000000 (0)
目标像素(蓝色)
- R: 00000000 (0)
- G: 00000000 (0)
- B: 11111111 (255)
执行 src OR dst 操作:
- R: 11111111 | 00000000 = 11111111 → 255
- G: 00000000 | 00000000 = 00000000 → 0
- B: 00000000 | 11111111 = 11111111 → 255
从二进制角度看这个操作的意义:
- 如果源或目标中某个颜色位为 1,那么最终颜色中该位就是 1。
- 这相当于将两个图像的“亮”部分(即非零值)合并在一起,不会覆盖或减弱。
// 创建一个带有透明通道的ARGB图像
QImage createTransparentImage(int width, int height) {
QImage image(width, height, QImage::Format_ARGB32);
image.fill(Qt::transparent); // 完全透明背景
return image;
}
// 绘制一个圆形光源(使用半透明颜色)
void drawLight(QPainter& painter, int x, int y, int radius) {
QRadialGradient gradient(x, y, radius);
gradient.setColorAt(0, QColor(255, 255, 0, 200)); // 中心黄色
gradient.setColorAt(1, Qt::transparent); // 边缘透明
painter.setBrush(gradient);
painter.setPen(Qt::NoPen);
painter.drawEllipse(QPoint(x, y), radius, radius);
}
void drawImage()
{
const int width = 800, height = 400;
// 创建透明背景图像
QImage result = createTransparentImage(width, height);
// 使用OR模式绘制多个光源
QPainter painter(&result);
painter.setCompositionMode(QPainter::RasterOp_SourceOrDestination);
// 绘制三个重叠的光源
drawLight(painter, 100, 100, 80);
drawLight(painter, 150, 150, 80);
drawLight(painter, 200, 100, 80);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.translate(400,0);
drawLight(painter, 100, 100, 80);
drawLight(painter, 150, 150, 80);
drawLight(painter, 200, 100, 80);
result.save("D://123.png");
}
这个代码模拟了"光照叠加"效果。绘制多个光源效果,当光源重叠时,亮度应该叠加(而不是简单的覆盖)。使用普通的合成模式会导致重叠区域被覆盖,而使用OR操作可以实现亮度叠加效果。
26、SourceAndDestination
将源和目标像素进行按位与(AND)操作。
原理:
QColor sourceAndrDestination(const QColor& src, const QColor& dst)
{
// 确保颜色都是预乘格式
QColor source = src.toRgb();
QColor destination = dst.toRgb();
// 对每个通道执行按位OR操作
int r = source.red() & destination.red();
int g = source.green() & destination.green();
int b = source.blue() & destination.blue();
// 返回结果颜色,保持原始alpha预乘状态
return QColor(r, g, b);
}
例1:绘制一个环形遮罩
// 创建环形遮罩
QImage createRingMask(int width, int height) {
QImage mask(width, height, QImage::Format_ARGB32);
mask.fill(Qt::transparent);
QPainter painter(&mask);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setBrush(Qt::white);
painter.setPen(Qt::NoPen);
// 白色的外圆
painter.drawEllipse(50, 50, 200, 200);
painter.setCompositionMode(QPainter::CompositionMode_Clear);
//清空内圆,形成环形
painter.drawEllipse(100, 100, 100, 100);
return mask;
}
// 创建彩色图像
QImage createColorImage(int width, int height) {
QImage image(width, height, QImage::Format_ARGB32);
QPainter painter(&image);
QLinearGradient gradient(0, 0, width, height);
gradient.setColorAt(0, Qt::red);
gradient.setColorAt(0.5, Qt::green);
gradient.setColorAt(1, Qt::blue);
painter.fillRect(image.rect(), gradient);
return image;
}
void drawImage()
{
const int width = 300, height = 300;
// 创建素材
QImage colorImage = createColorImage(width, height);
QImage ringMask = createRingMask(width, height);
QImage result(width, height, QImage::Format_ARGB32);
// 方法1:使用QPainter直接绘制
QPainter painter(&result);
painter.drawImage(0, 0, colorImage);
painter.setCompositionMode(QPainter::RasterOp_SourceAndDestination);
painter.drawImage(0, 0, ringMask);
// 方法2:使用模拟的函数手动混合
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
QColor src = colorImage.pixelColor(x, y);
QColor dst = ringMask.pixelColor(x, y);
result.setPixelColor(x, y, sourceAndrDestination(src, dst));
}
}
result.save("D://123.png");
}
例2:简易X光效果
class XRayViewer : public QWidget
{
Q_OBJECT
public:
XRayViewer(const QString &humanImagePath, const QString &xRayImagePath, QWidget *parent = nullptr)
: QWidget(parent),
bodyImage(humanImagePath),
boneMask(xRayImagePath)
{
connect(new QPushButton("切换显示", this), &QPushButton::clicked, this, &XRayViewer::toggleImage);
setMinimumSize(1024,512);
}
void paintEvent(QPaintEvent *event)
{
QPainter resultPainter(this);
resultPainter.drawImage(0, 0, bodyImage.convertToFormat(QImage::Format_Grayscale8));
if(showX)
{
// 使用AND操作只显示骨骼区域
resultPainter.setCompositionMode(QPainter::RasterOp_SourceAndDestination);
resultPainter.drawImage(0, 0, boneMask);
//增强骨骼亮度
resultPainter.setCompositionMode(QPainter::CompositionMode_Plus);
resultPainter.drawImage(0, 0, boneMask);
}
}
private:
void toggleImage()
{
showX = !showX;
update();
}
private:
QImage bodyImage, boneMask;
bool showX{false};
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString humanImagePath = "D://shou.png";
QString xRayImagePath = "D://shouX.png";
XRayViewer viewer(humanImagePath, xRayImagePath);
viewer.setWindowTitle("人体与X光图像切换");
viewer.show();
return a.exec();
}
这俩图片是AI生成的,不太精确,知道意思得了,X光图片用PS处理一下背景改成透明的。
27、SourceXorDestination
将源和目标像素进行按位异或(XOR)操作。
视觉特性:
1、可逆性:对同一图像应用两次 XOR 操作会恢复原状
XOR 运算有以下基本性质:
- 自反性:X ^ X = 0 (任何数与自己异或结果为0)
- 恒等性:X ^ 0 = X (与0异或保持不变)
- 结合律:(X ^ Y) ^ Z = X ^ (Y ^ Z)
位运算示例(单像素):
假设原始颜色值: 10110011 (179)
XOR图案值: 01100110 (102)
第一次XOR:
10110011
^
01100110
=
11010101 (213)第二次XOR:
11010101
^
01100110
=
10110011 (179) // 恢复原值
2、对比反转:与白色区域交互会产生颜色反转效果(X ^ 255 = 255 - X)
对于8位颜色通道(值范围0-255):
- 255的二进制表示为11111111
- XOR的真值表特性:0^1=1, 1^1=0
- 因此:X ^ 255相当于对X的每一位取反
3、差异突出:能突出显示两幅图像的差异部分
当且仅当两个位不同时,XOR结果为1,因此 A ^ B 的结果中:
- 为0的位表示A和B对应位相同
- 为1的位表示A和B对应位不同
QColor randomColor()
{
// 使用 QRandomGenerator 生成 0-255 之间的随机整数
int r = QRandomGenerator::global()->bounded(256);
int g = QRandomGenerator::global()->bounded(256);
int b = QRandomGenerator::global()->bounded(256);
int a = QRandomGenerator::global()->bounded(256);
return QColor(r, g, b, 255);
}
QColor xorBlendColors(const QColor& src, const QColor& dst) {
// 确保颜色是有效的RGB颜色(预处理)
QColor source = src.toRgb();
QColor destination = dst.toRgb();
// 对每个通道执行XOR运算(核心算法)
auto xorChannel = [](int src, int dst) {
// XOR运算(0-255范围)
int result = src ^ dst;
// 确保结果在合法范围内(实际上XOR结果不会越界,这里是为了防御性编程)
return std::clamp(result, 0, 255);
};
// 计算各通道
int r = xorChannel(source.red(), destination.red());
int g = xorChannel(source.green(), destination.green());
int b = xorChannel(source.blue(), destination.blue());
// 构造结果颜色(后处理)
return QColor(r, g, b);
}
void drawImage()
{
QImage image(700, 500, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
QColor dst = randomColor();
painter.fillRect(0, 0, 200, 200, dst);
painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
QColor src = randomColor();
painter.fillRect(100, 100, 200, 200, src);
QColor mixedColor = xorBlendColors(dst, src);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
// 绘制第三个矩形,使用混合后的颜色
painter.fillRect(350, 100, 200, 200, mixedColor);
image.save("D://123.png");
}
这个模拟函数展示了此模式的工作原理。
例:动态选框,特点是框的颜色可以叠加在任何背景上
class XORRubberBandWidget : public QWidget
{
QPoint start, end;
bool drawing = false;
public:
XORRubberBandWidget(QWidget *parent = nullptr) : QWidget(parent)
{
setWindowTitle("XOR Rubber Band - Corrected Version");
resize(800, 600);
setMouseTracking(true);
}
protected:
void paintEvent(QPaintEvent *) override
{
QPainter painter(this);
painter.fillRect(rect(),Qt::white);
painter.setBrush(Qt::blue);
for (int i = 0; i < 10; ++i)
{
painter.drawRect(50 + i*60, 100, 40, 40);
}
if (drawing && (start != end))
{
QPen pen(Qt::DotLine);
pen.setWidth(3);
pen.setColor(Qt::white/*black*/);
painter.setPen(pen);
painter.setBrush(Qt::NoBrush);
painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
painter.drawRect(QRect(start, end).normalized());
}
}
void mousePressEvent(QMouseEvent *ev) override {
if (ev->button() == Qt::LeftButton) {
start = end = ev->pos();
drawing = true;
update();
}
}
void mouseMoveEvent(QMouseEvent *ev) override {
if (drawing) {
end = ev->pos();
update();
}
}
void mouseReleaseEvent(QMouseEvent *ev) override {
if (ev->button() == Qt::LeftButton) {
drawing = false;
end = ev->pos();
update();
}
}
};
28、NotSourceAndNotDestination
进行三步操作:
- 对源像素取反
- 对目标像素取反
- 对上述两个结果进行逻辑与操作
原理:
QColor notSourceAndNotDestination(const QColor& src, const QColor& dst)
{
QColor source = src.toRgb();
QColor destination = dst.toRgb();
// 公式: result = (~src & ~dst) & 0xFF (确保在0-255范围内)
int r = (~source.red() & ~destination.red()) & 0xFF;
int g = (~source.green() & ~destination.green()) & 0xFF;
int b = (~source.blue() & ~destination.blue()) & 0xFF;
return QColor(r, g, b);
}
29、NotSourceOrNotDestination
进行三步操作:
- 对源像素取反
- 对目标像素取反
- 对上述两个结果进行逻辑或操作
视觉效果特点:
- 全白保留:只有当源像素和目标像素都为白色(1)时,结果才为黑色(0)
- 其他情况:任何一方或双方为黑色(0)时,结果都为白色(1)
原理:
QColor notSourceOrNotDestination(const QColor& src, const QColor& dst)
{
QColor source = src.toRgb();
QColor destination = dst.toRgb();
// 公式: result = (~src & ~dst) | 0xFF (确保在0-255范围内)
int r = (~source.red() | ~destination.red()) & 0xFF;
int g = (~source.green() | ~destination.green()) & 0xFF;
int b = (~source.blue() | ~destination.blue()) & 0xFF;
return QColor(r, g, b);
}
30、NotSourceXorDestination
进行两步操作:
- 对源像素取反
- 然后与目标像素进行异或操作
原理:
QColor notSourceXorDestination(const QColor& source, const QColor& destination)
{
QColor src = source.toRgb();
QColor dst = destination.toRgb();
return QColor(
(~src.red() ^ dst.red()) & 0xFF,
(~src.green() ^ dst.green()) & 0xFF,
(~src.blue() ^ dst.blue()) & 0xFF
);
}
31、NotSource
完全忽略目标像素值,仅反转源像素。
原理:
QColor notSource(const QColor& source, const QColor& destination)
{
QColor src = source.toRgb();
return QColor(
(~src.red() & 0xFF,
(~src.green() & 0xFF,
(~src.blue() & 0xFF
);
}
32、NotSourceAndDestination
源像素取反后再和目标像素进行与操作。
从真值表可以看出:此模式的特点是保留目标中与源透明区域重叠的部分。
原理:
QColor notSourceAndDestination(const QColor& source, const QColor& destination)
{
QColor src = source.toRgb();
QColor dst = destination.toRgb();
return QColor(
(~src.red() & dst.red()) & 0xFF,
(~src.green() & dst.green()) & 0xFF,
(~src.blue() & dst.blue()) & 0xFF
);
}
33、SourceAndNotDestination
目标像素取反后再和源像素进行与操作。
从真值表可以看出:此模式的特点是保留源中与目标透明区域重叠的部分。
原理:
QColor sourceAndNotDestination(const QColor& source, const QColor& destination)
{
QColor src = source.toRgb();
QColor dst = destination.toRgb();
return QColor(
(~dst.red() & src.red()) & 0xFF,
(~dst.green() & src.green()) & 0xFF,
(~dst.blue() & src.blue()) & 0xFF
);
}
34、NotSourceOrDestination
源像素取反再和目标像素进行或操作。
原理:
QColor notSourceOrDestination(const QColor& source, const QColor& destination)
{
QColor src = source.toRgb();
QColor dst = destination.toRgb();
return QColor(
(~src.red() | dst.red()) & 0xFF,
(~src.green() | dst.green()) & 0xFF,
(~src.blue() | dst.blue()) & 0xFF
);
}
35、SourceOrNotDestination
目标像素取反再和源像素进行或操作。
原理:
QColor sourceOrNotDestination(const QColor& source, const QColor& destination)
{
QColor src = source.toRgb();
QColor dst = destination.toRgb();
return QColor(
(src.red() | ~dst.red()) & 0xFF,
(src.green() | ~dst.green()) & 0xFF,
(src.blue() | ~dst.blue()) & 0xFF
);
}
36、ClearDestination
无条件清除,无论源像素和目标像素的值如何,结果像素值所有位都设为 0。
与 Clear 模式的区别:此模式的操作无关透明度而 Clear 模式把透明度也设置为 0。
原理:
QColor simulateClearDestination(const QColor& source, const QColor& destination)
{
Q_UNUSED(source);
Q_UNUSED(destination);
return QColor(0, 0, 0);
}
37、SetDestination
无条件填充,无论源像素和目标像素的值如何,结果总是1。
原理:
QColor simulateSetDestination(const QColor& source, const QColor& destination)
{
Q_UNUSED(source); // 明确标记参数未使用
Q_UNUSED(destination); // 明确标记参数未使用
// 始终返回完全不透明白色
return QColor(255, 255, 255);
}
38、NotDestination
完全忽略源像素值,仅反转目标像素。
原理:
QColor notDestination(const QColor& source, const QColor& destination)
{
QColor dst = destination.toRgb();
return QColor(
(~dst.red() & 0xFF,
(~dst.green() & 0xFF,
(~dst.blue() & 0xFF
);
}