QPainter::CompositionMode解析

基本概念

  • 目標(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 = 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

進行三步操作:

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917858.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917858.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917858.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

對接釘釘審批過程記錄(C#版本)

釘釘開放平臺&#xff1a;API總覽 - 釘釘開放平臺 按照開放平臺操作指引&#xff0c;進入到釘釘開發者后臺&#xff1a;開發者后臺統一登錄 - 釘釘統一身份認證&#xff0c;進行應用創建。 按照開放平臺指引下載釘釘SDK&#xff08;新版&#xff09;。 在vs引入釘釘dll文件。 獲…

AFSIM入門教程03.03:更新所有依賴庫版本

系列索引&#xff1a;AFSIM入門教程索引 上一篇中更新了tiff庫版本&#xff0c;本文將更新所有使用到的依賴庫版本。 失敗了 依賴庫 首先獲取哪些庫被使用了。打開源碼目錄&#xff0c;搜索# Configure the 3rd_party&#xff0c;可以看到調用第三方庫的代碼。 官方提供的…

完美解決hive external表中csv字段內容含“,“逗號的問題

為解決hive表中csv字段內容含","逗號的問題&#xff0c;網上幾乎都是說要用org.apache.hadoop.hive.serde2.OpenCSVSerde。 使用方法為&#xff1a; 1、mysql導出時&#xff0c;加一個ENCLOSED BY ‘"’&#xff0c; 示例&#xff1a; mysql -h 10.16.0.10 -P …

【Git】修改本地和遠程的分支名稱

其原理是&#xff1a; 對于本地&#xff1a;可直接修改分支名稱&#xff1b;對于遠程&#xff1a;不可直接重命名分支&#xff0c;所以應該將修改好名稱的分支以新分支的形式推送上遠程倉庫&#xff0c;之后將新分支與遠程新分支關聯&#xff0c;之后可選擇刪除舊分支# 例子&am…

ubuntu24.04安裝selenium、chrome、chromedriver

實驗環境&#xff1a;kaggle notebook、colab notebook1、安裝chrome!wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb!sudo dpkg -i google-chrome-stable_current_amd64.deb!sudo apt-get install -f!export QT_QPA_PLATFORMoffscreen!sudo…

西門子PLC基礎指令6:讀取時鐘指令、設置時鐘指令、使能含義與注意

讀/寫指令 1. 讀取時鐘 指令 READ_RTCREAD_RTC &#xff08;RTC 全稱是 Real - Time Clock&#xff0c;即實時時鐘 &#xff09;指令的主要作用是將 CPU 內部實時時鐘&#xff08;RTC&#xff09;的當前日期和時間信息讀取出來&#xff0c;并存儲到以指定字節地址&#xff08;圖…

GeoTools 結合 OpenLayers 實現緩沖區分析

前言? 緩沖區分析是地理信息系統&#xff08;GIS&#xff09;空間分析的核心功能之一。它通過圍繞點、線或面等地理實體&#xff0c;自動生成指定距離&#xff08;或寬度&#xff09;的等距區域&#xff08;緩沖區&#xff09;。該功能為量化空間鄰近度、評估影響范圍、識別潛…

SpringBoot 接入SSE實現消息實時推送的優點,原理以及實現

SpringBoot 接入SSE實現消息實時推送的優點,原理以及實現 前言 上一篇文章 我寫的關于SpringBoot整合t-io是websocket實時通信的文章中我們可以了解到 websocket是雙向通信的,而且需要TCP連接的支持,今天在這里我要說的SSE(Server-Sent Events) 是一個單項通信的消息實時推…

創建型設計模式:對象誕生的藝術與智慧

&#x1f3ad; 創建型設計模式&#xff1a;對象誕生的藝術與智慧 &#x1f4a1; 溫馨提示&#xff1a;本文將以輕松有趣的方式帶你探索設計模式的世界&#xff0c;就像在聽一個關于"如何優雅地生孩子"的故事一樣&#xff01; &#x1f6aa; 傳送門&#xff1a;在開始…

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘gensim’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘gensim’問題 摘要 在使用 PyCharm 2025 進行 Python 開發時&#xff0c;常常需要通過 pip install 安裝第三方包以豐富項目功能。但在安裝 gensim 等包時&…

【嵌入式電機控制#26】BLDC:三相模擬采集

之前有些網友試著用有刷的平均電流法采集三相&#xff0c;還搞了個閉環控制。求&#xff01;結果直接把驅動板給干沒了......做過仿真的朋友們都知道&#xff0c;無刷電機的相電流波形是介于方波和正弦波的。如果拿平均電流去測量&#xff0c;很不靠譜。這節內容為大家分享采集…

ref存儲對象和reactive深度響應式遞歸地對對象的嵌套屬性進行響應式處理

ref 不會遞歸地對 對象 或 數組 中的每個屬性或元素進行深度響應式處理。如果你需要遞歸處理嵌套屬性&#xff0c;reactive 是更適合的選擇。讓我通過具體的例子來展示這一點。 例子&#xff1a;ref 存儲對象和嵌套對象 1. 使用 ref 存儲嵌套對象&#xff1a; import { createA…

小鵬汽車前端面經

前端基礎與瀏覽器機制 (Front-End Fundamentals & Browser Mechanics) 這些問題涵蓋了Web工作的基本原理&#xff0c;包括網絡、渲染和瀏覽器特性。 1. 瀏覽器渲染與性能 (Browser Rendering & Performance) URL輸入發生什么&#xff1f;(What happens when you type a…

利用DeepSeek編寫go語言按行排序程序

聽說google出品的go語言也是系統開發中常用的&#xff0c;性能到底怎么樣&#xff0c;還是老辦法&#xff0c;讓DeepSeek寫個程序跑一下&#xff0c;基于以往的經驗&#xff0c;直接讓它同時編寫有緩沖和無緩沖版本。 package mainimport ("bufio""fmt"&qu…

《解構Angular組件變化檢測:從自動到手 動的效能突破》

為何有時數據更新后視圖卻無動于衷?為何看似簡單的操作會引發連鎖式的性能損耗?要解開這些疑問,需要穿透表層的API調用,深入到框架設計的底層邏輯中去。變化檢測的核心使命,是確保視圖層能夠準確反映數據層的當前狀態。這種"數據-視圖"的同步關系,是所有前端框…

書單 | AI編程+Python+Go三大核心領域書單

這份書單聚焦AI編程、Python開發、Go語言三大核心領域&#xff0c;精選6本本月 最具前瞻性的技術圖書&#xff0c;為你構建"工具鏈業務層系統層"的全棧能力。 1、人人皆可Vibe編程&#xff1a;玩轉氛圍編程 作者&#xff1a;池志煒,薛志榮 本書圍繞Vibe編程這一AI驅…

Kali Linux 2025.2基于MITRE ATTCK框架

從徹底革新的菜單結構到新增的13款尖端工具&#xff0c;再到首次支持智能手表Wi-Fi注入&#xff0c;Kali Linux 2025.2為紅隊、藍隊以及安全研究人員提供了更高效、更直觀的工具生態。菜單結構大變革&#xff1a;基于MITRE ATT&CK框架Kali Linux 2025.2最引人注目的變化之一…

javacc實現簡單SQL解析器

文章目錄前言本章節源碼需求1&#xff1a;實現一個最簡單的select sql要求實現jj文件編譯測試需求2&#xff1a;理解Token及其他屬性說明javajj文件需求3&#xff1a;實現解析得到SQL語法樹 & 精確點位資料獲取前言 博主介紹&#xff1a;?目前全網粉絲4W&#xff0c;csdn…

Odoo OWL前端框架全面學習指南 (后端開發者視角)

核心理念&#xff1a; 將您熟悉的Odoo后端MVCORM架構思想&#xff0c;完整映射到前端OWL組件化開發中&#xff0c;讓您在熟悉的概念體系下&#xff0c;快速掌握新的技術棧。第一部分&#xff1a;核心概念映射與環境搭建內容摘要&#xff1a; 本部分旨在建立后端與前端最核心的概…

Java開發工具包,jdk,idea,VMware,rocketmq,redis,CentOS7

Java開發工具包&#xff0c;jdk&#xff0c;idea&#xff0c;VMware&#xff0c;rocketmq&#xff0c;redis&#xff0c;CentOS7 下載地址 通過網盤分享的文件&#xff1a;Java開發環境工具包 鏈接: https://pan.baidu.com/s/1eJqvPx5DYqtmXgmEtOl8-A?pwdcj1f 提取碼: cj1f –…