QPainter::CompositionMode解析

发布于:2025-08-04 ⋅ 阅读:(14) ⋅ 点赞:(0)

基本概念

  • 目标(Destination):已经存在的像素。
  • 源(Source):要绘制的新像素。
  • 组合模式:决定源和目标如何混合。

总结

  1. SourceOver:源绘制在目标之上。
  2. DestinationOver:目标绘制在源之上。
  3. Clear:二者重叠区域被清空为透明
  4. Source结果完全由源决定。
  5. Destination结果完全由目标决定。
  6. SourceIn:输出RGB值由源决定,且受到目标的透明度影响,只有在目标不透明处才显示。
  7. DestinationIn:输出RGB值由目标决定,且受到源的透明度影响。在源的透明区域,目标不保留。
  8. SourceOut:输出RGB值由源决定,且受到目标的不透明度的补集调制。结果在目标完全不透明的区域不显示。
  9. DestinationOut:输出RGB值由目标决定,且受到源的不透明度的补集调制。结果在源完全不透明的区域不显示
  10. SourceAtop:只在目标不透明的区域绘制,目标只在源透明的区域保留。
  11. DestinationAtop:只在源不透明的区域绘制,源只在目标透明的区域保留。
  12. Xor:源和目标在彼此透明的区域显示,在都不透明的区域变透明。

  13. Plus:相当于 Photoshop 中的“线性减淡”混合模式。
  14. Multiply:相当于 Photoshop 中的“正片叠底”混合模式。
  15. Screen:相当于 Photoshop 中的“滤色”混合模式。
  16. Overlay:相当于 Photoshop 中的“叠加”混合模式。
  17. Darken:相当于 Photoshop 中的“变暗”混合模式。
  18. Lighten:相当于 Photoshop 中的“变亮”混合模式。
  19. ColorDodge:相当于 Photoshop 中的“颜色减淡”混合模式。
  20. ColorBurn:相当于 Photoshop 中的“颜色加深”混合模式。
  21. HardLight:相当于 Photoshop 中的“强光”混合模式。
  22. SoftLight:相当于 Photoshop 中的“柔光”混合模式。
  23. Difference:相当于 Photoshop 中的“差值”混合模式。
  24. Exclusion:相当于 Photoshop 中的“排除”混合模式。

  25. SourceOrDestination:位运算,结果 = Source | Destination
  26. SourceAndDestination:位运算,结果 = Source & Destination
  27. SourceXorDestination:位运算,结果 = Source ^ Destination
  28. NotSourceAndNotDestination:位运算,结果 = (~Source) & (~Destination)
  29. NotSourceOrNotDestination:位运算,结果 = (~Source) | (~Destination)
  30. NotSourceXorDestination:位运算,结果 = (~Source) ^ Destination
  31. NotSource:位运算,结果 = ~Source
  32. NotSourceAndDestination:位运算,结果 = (~Source) & Destination
  33. SourceAndNotDestination:位运算,结果 = Source & (~Destination)。 
  34. NotSourceOrDestination:位运算,结果 = (~Source) | Destination
  35. SourceOrNotDestination:位运算,结果 = Source | (~Destination)。 
  36. ClearDestination:无视目标像素和源像素,结果像素值所有位都设为 0。
  37. SetDestination:无视目标像素和源像素,结果像素值所有位都设为 1。
  38. 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

进行三步操作:

  1. 对源像素取反
  2. 对目标像素取反
  3. 对上述两个结果进行逻辑与操作

原理:

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. 对源像素取反
  2. 对目标像素取反
  3. 对上述两个结果进行逻辑或操作

视觉效果特点:

  • 全白保留:只有当源像素和目标像素都为白色(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

进行两步操作:

  1. 对源像素取反
  2. 然后与目标像素进行异或操作

原理:

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
    );
}

网站公告

今日签到

点亮在社区的每一天
去签到