目錄html
算法參考自:【RGBA alpha 透明度混合算法】 ,下面的敘述和實現中有一些我的修改在裏面。算法
R一、G一、B一、Alpha1 爲前景顏色值,R二、G二、B二、Alpha2 爲背景顏色值,則:bash
Alpha = 1 - (1 - Alpha1) * ( 1 - Alpha2) R = (R1 * Alpha1 + R2 * Alpha2 * (1-Alpha1))/Alpha G = (G1 * Alpha1 + G2 * Alpha2 * (1-Alpha1))/Alpha B = (B1 * Alpha1 + B2 * Alpha2 * (1-Alpha1))/Alpha
這裏的Alpha取值範圍是[0,1],須要使用到浮點計算(實數計算)。對於咱們常見的8位圖像,咱們能夠將其值域改成[0,255]進行計算,具體的見下面測試代碼。佈局
混合算法目前在經常使用到的算法是AlphaBlend。
計算公式以下:假設一幅圖像是A,另外一幅透明的圖像是B,那麼透過B去看A,看上去的圖像C就是B和A的混合圖像,
設B圖像的透明度爲alpha
(取值爲0-1,1爲徹底透明,0爲徹底不透明).
Alpha
混合公式以下:測試
R(C)=(1-alpha)*R(B) + alpha*R(A) G(C)=(1-alpha)*G(B) + alpha*G(A) B(C)=(1-alpha)*B(B) + alpha*B(A)
R(x)、G(x)、B(x)分別指顏色x的RGB份量原色值。從上面的公式能夠知道,Alpha實際上是一個決定混合透明度的數值。ui
這裏只對B圖的Alpha進行了處理,但A圖自己若是也有透明通道的,也須要進行同樣的處理,即.net
A(C)=(1-alpha)*A(B) + alpha*A(A)
首先,要能取得上層與下層顏色的 RGB三基色,而後用r,g,b 爲最後取得的顏色值;r一、g一、b1是上層的顏色值;r二、g二、b2是下層顏色值,若Alpha=上層透明度,則:code
當Alpha=50%時orm
r = r1/2 + r2/2; g = g1/2 + g2/2; b = b1/2 + b2/2;
當Alpha<50%時htm
r = r1 - r1/Alpha + r2/Alpha; g = g1 - g1/Alpha + g2/Alpha; b = b1 - b1/Alpha + b2/Alpha;
當Alpha>50%時
r = r1/Alpha + r2 - r2/Alpha; g = g1/Alpha + g2 - g2/Alpha; b = b1/Alpha + b2 - b2/Alpha;
這個算法比較簡單,我也沒有去作實現。簡單來講這裏就是看透明度高是否超過50%,來決定是上下層圖像誰占主導地位。
實現實際上是很簡單的,只是注意下面沒有實數的計算,都是使用的整數計算,要注意移位與抹零的操道別出錯。
下面的rgba1
表示前景圖(個人代碼裏是水印圖)的一個像素值,是RGBA8888格式的。rgba2
表示背景圖的一個像素值。a1
表示rgba1
中的Alpha
份量值,a2
表示rgba2
中的Alpha
份量值。
// 若是alpha的值域是[0,1],這裏至關於將其拉伸爲[0,255] // 因此至關因而 Alpha = 1 - (1 - Alpha1) * ( 1 - Alpha2)乘以了兩次255 // 當a1和a2都接近於0的時候,會致使計算獲得的A值不爲0,致使疊加不正常 uint32_t A = (0xffff - (0xff - a1)*(0xff - a2)); // 下面左邊部分少左移8位,至關於乘以了255 uint32_t R = (((rgba1 << 8 &0xff00U) * a1 + (rgba2 >> 0 &0xffU) * a2 *(0xff - a1))/A)&0xffU; uint32_t G = (((rgba1 >> 0 &0xff00U) * a1 + (rgba2 >> 8 &0xffU) * a2 *(0xff - a1))/A)&0xffU; uint32_t B = (((rgba1 >> 8 &0xff00U) * a1 + (rgba2 >> 16&0xffU) * a2 *(0xff - a1))/A)&0xffU;
uint32_t A = a1; uint32_t R = (((rgba1 >> 0 &0xffU) * A + (rgba2 >> 0 &0xffU) *(0xff - A)) >> 8)&0xffU; uint32_t G = (((rgba1 >> 8 &0xffU) * A + (rgba2 >> 8 &0xffU) *(0xff - A)) >> 8)&0xffU; uint32_t B = (((rgba1 >> 16&0xffU) * A + (rgba2 >> 16&0xffU) *(0xff - A)) >> 8)&0xffU; A = ((a1 * A + a2 *(0xff - A)) >> 8)&0xffU; // 必須對Alpha波段也處理
#include <QApplication> #include <QWidget> #include <QLineEdit> #include <QPushButton> #include <QVBoxLayout> #include <QHBoxLayout> #include <QFileDialog> #include <QWebEngineView> #include <QXmlStreamWriter> #include <QBuffer> int main(int argc, char *argv[]) { QApplication a(argc, argv); QImage bkImage,wmImage; QImage mixImage1,mixImage2; // 建立窗口 QWidget widget; // 添加控件 QWebEngineView *wevView = new QWebEngineView(&widget); QLineEdit* leBkImagePath = new QLineEdit(&widget); QLineEdit* leWmImagePath = new QLineEdit(&widget); QPushButton* pbSelectBkFile = new QPushButton(QStringLiteral("選擇背景圖"),&widget); QPushButton* pbSelectWmFile = new QPushButton(QStringLiteral("選擇水印圖"),&widget); QPushButton* pbRunMix = new QPushButton(QStringLiteral("執行疊加"),&widget); //pbRunDetect->setEnabled(false); QHBoxLayout* hbLayout = new QHBoxLayout; // 設置佈局 hbLayout->addWidget(leBkImagePath); hbLayout->addWidget(pbSelectBkFile); hbLayout->addWidget(leWmImagePath); hbLayout->addWidget(pbSelectWmFile); hbLayout->addWidget(pbRunMix); QVBoxLayout* vbLayout = new QVBoxLayout(&widget); vbLayout->addLayout(hbLayout); vbLayout->addWidget(wevView); // 添加處理操做 std::function<void()> updateHtmlView = [wevView,&bkImage,&wmImage,&mixImage1,&mixImage2]() { QByteArray html; { QXmlStreamWriter writer(&html); writer.setAutoFormatting(true); writer.writeStartDocument(); writer.writeStartElement("html"); writer.writeStartElement("body"); writer.writeAttribute("bgcolor","gray"); QStringList imgName = { QStringLiteral("背景圖"), QStringLiteral("水印圖"), QStringLiteral("算法1結果圖"), QStringLiteral("算法2結果圖") }; QList<QImage*> imgRef = { &bkImage,&wmImage,&mixImage1,&mixImage2 }; for(int i=0;i<imgName.size();++i){ if(imgRef[i]->isNull()){continue;} writer.writeTextElement("h2",imgName[i]); writer.writeStartElement("img"); QBuffer buffer; imgRef[i]->save(&buffer,"PNG"); writer.writeAttribute("src","data:image/png;base64," + buffer.data().toBase64()); writer.writeEndElement(); } writer.writeEndElement(); writer.writeEndElement(); } wevView->setHtml(QString::fromUtf8(html)); }; QObject::connect(pbSelectBkFile,&QPushButton::clicked, [leBkImagePath,&bkImage,&widget,&updateHtmlView]() { static QString path("."); path = QFileDialog::getOpenFileName(&widget, QStringLiteral("選擇背景圖"), path, QString("Images (*.png *.jpg *.jpeg *.jfif)")); if(path.isEmpty()){return;} QImage image; if(!image.load(path)){return;} bkImage = image.convertToFormat(QImage::Format_RGBA8888); leBkImagePath->setText(path); updateHtmlView(); }); QObject::connect(pbSelectWmFile,&QPushButton::clicked, [leWmImagePath,&bkImage,&wmImage,&widget,&updateHtmlView]() { static QString path("."); path = QFileDialog::getOpenFileName(&widget, QStringLiteral("選擇水印圖"), path, QString("Images (*.png)")); if(path.isEmpty()){return;} QImage image; if(!image.load(path)){return;} // 水印圖不能比背景圖大 int w = image.width() * 2 > bkImage.width() ? bkImage.width()/2:image.width(); int h = image.height() * w / image.width(); h = h > bkImage.height()?bkImage.height():h; wmImage = image.scaledToHeight(h).convertToFormat(QImage::Format_RGBA8888); leWmImagePath->setText(path); updateHtmlView(); }); QObject::connect(pbRunMix,&QPushButton::clicked, [&bkImage,&wmImage,&mixImage1,&mixImage2,&updateHtmlView] { mixImage1 = mixImage2 = bkImage; for(int r = 0;r < wmImage.height();++r){ uint32_t* pBgLine = reinterpret_cast<uint32_t*>(bkImage.bits() + bkImage.bytesPerLine()*r); uint32_t* pWmLine = reinterpret_cast<uint32_t*>(wmImage.bits() + wmImage.bytesPerLine()*r); uint32_t* pM1Line = reinterpret_cast<uint32_t*>(mixImage1.bits() + mixImage1.bytesPerLine()*r); uint32_t* pM2Line = reinterpret_cast<uint32_t*>(mixImage2.bits() + mixImage2.bytesPerLine()*r); for(int c=0;c<wmImage.width();++c){ uint32_t rgba1 = pWmLine[c]; uint32_t rgba2 = pBgLine[c]; uint32_t a1 = rgba1 >> 24; uint32_t a2 = rgba2 >> 24; { // 若是alpha的值域是[0,1],這裏至關於將其拉伸爲[0,255] // 因此至關因而 Alpha = 1 - (1 - Alpha1) * ( 1 - Alpha2)乘以了兩次255 uint32_t A = (0xffff - (0xff - a1)*(0xff - a2)); // 下面左邊部分少左移8位,至關於乘以了255 uint32_t R = (((rgba1 << 8 &0xff00U) * a1 + (rgba2 >> 0 &0xffU) * a2 *(0xff - a1))/A)&0xffU; uint32_t G = (((rgba1 >> 0 &0xff00U) * a1 + (rgba2 >> 8 &0xffU) * a2 *(0xff - a1))/A)&0xffU; uint32_t B = (((rgba1 >> 8 &0xff00U) * a1 + (rgba2 >> 16&0xffU) * a2 *(0xff - a1))/A)&0xffU; pM1Line[c] = R|(G<<8)|(B<<16)|((A&0xff)<<24); } { uint32_t A = a1; uint32_t R = (((rgba1 >> 0 &0xffU) * A + (rgba2 >> 0 &0xffU) *(0xff - A)) >> 8)&0xffU; uint32_t G = (((rgba1 >> 8 &0xffU) * A + (rgba2 >> 8 &0xffU) *(0xff - A)) >> 8)&0xffU; uint32_t B = (((rgba1 >> 16&0xffU) * A + (rgba2 >> 16&0xffU) *(0xff - A)) >> 8)&0xffU; A = ((a1 * A + a2 *(0xff - A)) >> 8)&0xffU; // 必須對Alpha波段也處理 pM2Line[c] = R|(G<<8)|(B<<16)|(A<<24); } } } updateHtmlView(); }); widget.resize(1024,768); widget.show(); return a.exec(); }