QPainter::CompositionMode解析
基本概念
- 目标(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 = 0a_out = 0.0;break;case QPainter::CompositionMode_Source: // [1]// R = Cs// A = Asr_out = r_src;g_out = g_src;b_out = b_src;a_out = a_src;break;case QPainter::CompositionMode_Destination: // [2]// R = Cd// A = Adr_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*Adr_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*Asr_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) = Adr_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) = Asr_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:// 默认使用 SourceOverr_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));// 根据条件绘制或不绘制logoQRect logoRect(20, 20, 100, 50);bool shouldDrawLogo = false;{painter.save();if(!shouldDrawLogo){painter.setCompositionMode(QPainter::CompositionMode_Destination);}// 正常绘制logopainter.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. 设置合成模式为 SourceOutpainter.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. 设置合成模式为 SourceAtoppainter.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));// 设置合成模式为 Darkenpainter.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);
}