Qt 图像合成模式(一): Porter-Duff 规则

1. Qt 图像合成模式(一) Composition_Modes_Demo

​ Qt的图像的合成模式部分讲得太简单了,给出了一个简单的案例,还没有代码,demo里有个很全面的案例,但是又太复杂了,让人看得心生厌烦。尝试总结一些过度性的知识,并为自己梳理一下相关的内容。

1.1 官方档案内容摘要

  • QPainter提供了CompositionMode枚举,该枚举定义了用于数字图像合成的 Porter-Duff 规则; 它描述了一种模型,用于将一个图像( 源)中的像素与另一图像( 目标)中的像素进行组合。

  • 几种合成模式需要在源图像或目标图像中使用alpha通道才能生效。

  • 为了获得最佳性能,首选格式Format_ARGB32_Premultiplied。任何其他格式,包括QImage :: Format_ARGB32,都具有明显较差的性能。 默认情况下,此引擎用于 QWidget 和 QPixmap。

1.2 enum QPainter::CompositionMode

ConstantValueDescription
QPainter::CompositionMode_SourceOver0这是默认模式。源的Alpha用于在目标顶部混合像素。
QPainter::CompositionMode_DestinationOver1目标的Alpha用于将其混合在源像素的顶部。此模式与CompositionMode_SourceOver相反。
QPainter::CompositionMode_Clear2独立于源,清除目标中的像素( 将其设置为完全透明)。
QPainter::CompositionMode_Source3输出是源像素。 ( 这意味着基本的复制操作,并且在源像素不透明时与SourceOver相同)。
QPainter::CompositionMode_Destination4输出是目标像素。这意味着混合无效。此模式与CompositionMode_Source相反。
QPainter::CompositionMode_SourceIn5输出是源,其中的alpha值将减去目标的alpha值。
QPainter::CompositionMode_DestinationIn6输出是目标,其中的alpha值被源的alpha值减小。此模式与CompositionMode_SourceIn相反。
QPainter::CompositionMode_SourceOut7输出是源,其中alpha减小了目标的倒数。
QPainter::CompositionMode_DestinationOut8输出是目标,其中的alpha值由源的倒数减少。此模式与CompositionMode_SourceOut相反。
QPainter::CompositionMode_SourceAtop9源像素在目标顶部混合,源像素的alpha减少了目标像素的alpha。
QPainter::CompositionMode_DestinationAtop10目标像素混合在源的顶部,目标像素的alpha减小目标像素的alpha。此模式与CompositionMode_SourceAtop相反。
QPainter::CompositionMode_Xor11源( 其alpha值与目标alpha值的倒数成反比)与目标合并,该目标的alpha值与源alpha的倒数成反比。 CompositionMode_Xor与按位Xor不同。
QPainter::CompositionMode_Plus12源像素和目标像素的alpha和颜色都被加在一起。
QPainter::CompositionMode_Multiply13输出是源颜色乘以目标颜色。将颜色与白色相乘会使颜色保持不变,而颜色与黑色相乘则会产生黑色。
QPainter::CompositionMode_Screen14将源颜色和目标颜色反转,然后相乘。用白色屏蔽颜色会产生白色,而用黑色屏蔽颜色会使颜色保持不变。
QPainter::CompositionMode_Overlay15根据目标颜色乘以或筛选颜色。将目标颜色与源颜色混合以反映目标的明暗。
QPainter::CompositionMode_Darken16选择了较深的源颜色和目标颜色。
QPainter::CompositionMode_Lighten17选择了源色和目标色的浅色。
QPainter::CompositionMode_ColorDodge18目标颜色变亮以反映源颜色。黑色源颜色使目标颜色保持不变。
QPainter::CompositionMode_ColorBurn19目标颜色变暗以反映源颜色。白色源颜色使目标颜色保持不变。
QPainter::CompositionMode_HardLight20根据源颜色乘以或筛选颜色。光源颜色将使目标颜色变亮,而光源源颜色将使目标颜色变暗。
QPainter::CompositionMode_SoftLight21根据源颜色使颜色变暗或变亮。类似于CompositionMode_HardLight。
QPainter::CompositionMode_Difference22从较浅的颜色中减去较深的颜色。用白色绘画会反转目标颜色,而使用黑色绘画会使目标颜色保持不变。
QPainter::CompositionMode_Exclusion23与CompositionMode_Difference相似,但对比度较低。用白色绘画会反转目标颜色,而使用黑色绘画会使目标颜色保持不变。
QPainter::RasterOp_SourceOrDestination24对源像素和目标像素( src OR dst)进行按位或运算。
QPainter::RasterOp_SourceAndDestination25对源像素和目标像素( src AND dst)进行按位与运算。
QPainter::RasterOp_SourceXorDestination26对源像素和目标像素进行按位XOR操作( src XOR dst)。
QPainter::RasterOp_NotSourceAndNotDestination27对源像素和目标像素进行按位“或非”运算( ( NOT src)AND( NOT dst))。
QPainter::RasterOp_NotSourceOrNotDestination28在源像素和目标像素上执行按位与非运算( ( NOT src)OR( NOT dst))。
QPainter::RasterOp_NotSourceXorDestination29进行按位运算,其中将源像素反转,然后与目标( ( NOT src)XOR dst)进行异或。
QPainter::RasterOp_NotSource30在源像素反转的情况下执行按位运算( NOT src)。
QPainter::RasterOp_NotSourceAndDestination31进行按位运算,其中源反转,然后与目标( ( NOT src)AND dst)进行“与”运算。
QPainter::RasterOp_SourceAndNotDestination32执行按位运算,其中源与反转的目标像素( src AND( NOT dst))进行“与”运算。
QPainter::RasterOp_NotSourceOrDestination33进行按位运算,其中源反转,然后与目标( ( NOT src)OR dst)进行“或”运算。
QPainter::RasterOp_ClearDestination35清除目标中的像素( 设置为0),与源无关。
QPainter::RasterOp_SetDestination36目标中的像素独立于源进行设置( 设置为1)。
QPainter::RasterOp_NotDestination37在目标像素反转的情况下进行按位运算( 不是dst)。
QPainter::RasterOp_SourceOrNotDestination34执行按位运算,其中源与反转的目标像素进行“或”运算( src OR( NOT dst))。

1.3 山寨Qt合成模式图片的代码

​ 在Qt的示例中,死活没有找到与如下图片相关的代码,然后自己写了一个,以假乱真,可以供参考。

​ 代码很简单,就一个QWidget做界面,在上面 绘制 目标图像 + 源图像。
在这里插入图片描述
class CompositionWidget : public QWidget

#include "CompositionWidget.h"

static const int side = 15;
static const int IMG_Width = 9 * side;
static const int IMG_Height = 12 * side;
static int cm[12] = {
    3,4,0,1,5,6,
    7,8,9,10,2,11
};

QString topic[12]={
    "Source","Destination","SourceOver","DestinationOver","SourceIn","DestinationIn",
    "SourceOut","DestinationOut","SourceAtop","DestinationAtop","Clear","Xor"
};

CompositionWidget::CompositionWidget(QWidget *parent)
    : QWidget(parent)
{
    resize((IMG_Width+10)*6+10,(IMG_Height+10) *2+10);
}

void CompositionWidget::paintEvent(QPaintEvent */*event*/)
{
    QImage SourceIMG(IMG_Width,IMG_Height,QImage::Format_ARGB32);
    QImage DestIMG(IMG_Width,IMG_Height,QImage::Format_ARGB32);

    QPolygon destPolygon,sourcePolygon;
    destPolygon << QPoint(0,0) << QPoint(IMG_Width,0) << QPoint(0,IMG_Width);
    sourcePolygon << QPoint(0,0) << QPoint(IMG_Width,0) << QPoint(IMG_Width,IMG_Width);


    QImage BackIMG(IMG_Width,IMG_Height,QImage::Format_ARGB32);
    BackIMG.fill(Qt::white);
    QPainter painter;

    QFont font("times new roman",11,75);

    painter.begin(&BackIMG);
    painter.setBrush(QColor(239,239,239));
    painter.setPen(Qt::NoPen);
    for (int  x= 0;  x< IMG_Width; x+=2*side) {
        for (int y = 0,row = 0; y < IMG_Height; y+=side,row++) {
            painter.drawRect(x + (row % 2) * side,y,side,side);
        }
    }
    painter.end();

    for (int i = 0; i < 12; ++i) {
        SourceIMG.fill(0);
        DestIMG.fill(0);
        painter.begin(&SourceIMG);
        painter.setPen(Qt::NoPen);
        painter.setBrush(QColor(98,136,61));
        painter.drawPolygon(sourcePolygon);
        painter.end();


        painter.begin(&DestIMG);
        painter.save();
        painter.setPen(Qt::NoPen);
        painter.setBrush(QColor(220,157,57));
        painter.drawPolygon(destPolygon);
        painter.setCompositionMode(QPainter::CompositionMode(cm[i]));
        painter.drawImage(0,0,SourceIMG);

        painter.restore();
        painter.setPen(Qt::black);
        painter.setFont(font);
        painter.setRenderHint(QPainter::TextAntialiasing);
        painter.drawText(QRect(0,0,IMG_Width,IMG_Height-10),Qt::AlignBottom | Qt::AlignHCenter,topic[i]);
        painter.end();

        painter.begin(this);
        painter.drawImage( (IMG_Width+10)* (i % 6)+10,(IMG_Height+10)*(i/6)+10,BackIMG);
        painter.drawRect((IMG_Width+10)* (i % 6)+10,(IMG_Height+10)*(i/6)+10,IMG_Width,IMG_Height);
        painter.drawImage( (IMG_Width+10)* (i % 6)+10,(IMG_Height+10)*(i/6)+10,DestIMG);
        painter.end();
    }
}

1.4 Porter-Duff 规则

​ 1984 年 7 月《计算机图形》杂志 18 期 253 - 259 页,刊登了Porter ThomasDuff Tom 的论文《Compositing Digital Ima-ges》,文中讲述了12个合成规则,这些规则都是基于一些简单的数学等式。

​ 文中提出一个重要的alpha channel的概念,并定义了源图像(正在绘制的基元)和目标图像(图形区域)的像素的alpha分量和颜色的

注意:分量和通道(Components and Channels)。颜色由三个值编码,这些值也称为 分量 或 通道。软件中最普通的编码称为RGB,它使用红色、绿色和蓝色分量。Yuv是另外一种编码 方式,它使用一个亮度通道(Y)和两个色度通道(u和v)。alpha通道(或alpha分量)是第四种分量,独立于颜色编码之外,它定义颜色的半透明或 不透明的级别。

​ 下面的内容,是借用了 《Filthy Rich Clients: Developing Animated and Graphical Effects for Desktop JAVA Applications》中文名《java极富客户端》书中的内容来写的。书中总结归纳的很简单一看就懂。

​ 下面的Porter和Duff的12个规则,每个规则都有一个简短的公式,每个案例都描述了在一个蓝色矩形(目标图像)上绘制一个红色椭圆的图像(源图像)。

1.4.1 Source Over (源覆盖)

这是默认模式。源的Alpha用于在目标顶部混合像素。

​ 要决定在什么时候使用什么规则,理解文档中提到的Porter-Duff等式很重要。为了避免使您对数据描述讨厌得要死(还因为我对考虑这12个等式也很 头痛),让我们集中在最有用的规则之一 uSource Over (源覆盖)”上,它在目标图像之上绘制源图像,源图像好像是覆盖在目标图像上的一片玻璃上的一个半透明图画。

​ 描述这个规则的等式 如下所示:
Ar = As + Ad * ( 1 - As )
Cr = Cs + Cd * ( 1 - As )

​ 因子 A 代表这个像素的alpha通道,C代表这个像素的每个颜色分量。
​ 下标 r、s 和 d分别 代表这个像素的 结果(result)、 (source) 和 目标 (destination)。
​ As 代表(绘制到图形区域的基元) 的alpha通道,
​ Ad 代表已经在图形区域上的像素的alpha 通道。
​ Ar 最终计算结果的alpha通道。这个等式中的所有值都是在0.0和1.0之间的 浮点数,计算结果也位于这个范围。

​ **注意1:**在实际代码中,每个分量都是一个在0到255之间的整数值,而不是0. 0和1. 0之间的浮点数。
​ **注意2:**预乘的分量。注意到Porter-Duff等式都定义为操作与相应的alpha分量预乘的颜色 分量,这很重要。

​ 例、如果我们在一个蓝色矩形上绘制一个半透明的红色矩形,那将会发生什么?让我们着手把这些等式写成完全由每个颜色分量代表的代码:

int main(int argc,char *argv[])
{
    int srcA = 127 ; //semi-opaque source 半透明源
    int srcR =255 ; //full red 全部红色
    int srcG = 0 ; //no green 没有绿色
    int srcB = 0 ; //no blue 没有蓝色
    int dstA = 255 ; //fully opaque destination 完全不透明的目白勺地
    int dstR = 0 ; //no red 没有红色
    int dstG = 0 ; //no green 没有绿色
    int dstB =255 ; //full blue 全部蓝色

    srcR = (srcR * srcA) /255 ; //premultiply srcR 预乘 srcA
    srcG = (srcG * srcA) /255 ; //premultiply srcR 预乘 srcA
    srcB = (srcB * srcA) /255 ; //premultiply srcR 预乘 srcA

    dstR = (dstR * dstA) /255 ; //premultiply dstR 预乘 dstR
    dstG = (dstG * dstA) /255 ; //premultiply dstG 预乘 dstG
    dstB = (dstB * dstA) /255 ; //premultiply dstB 预乘 dstB

    int resultA = srcA + (dstA * (255 - srcA) ) /255;
    int resultR = srcR + (dstR * (255 - srcA) ) /255;
    int resultG = srcG + (dstG * (255 - srcA) ) /255;
    int resultB = srcB + (dstB * (255 - srcA) ) /255;

    qDebug() << resultA << resultR << resultG << resultB;

    return 0;
}

生成的结果 : 255 127 0 128

生成的颜色为完全不透明的洋红色,就像把一张半透明的红色纸片放在蓝色背景上一样。

在这里插入图片描述

1.4.2 Clear

独立于源,清除目标中的像素( 将其设置为完全透明)。

描述这个规则的等式 如下所示:
Ar =0
Cr =0

目标中的颜色和alpha都被清除掉。无论用什么颜色或形状来绘制,被源覆盖的每个目的地 的像素都会消失,如图所示。
在这里插入图片描述

1.4.3 Destination

输出是目标像素。这意味着混合无效。此模式与CompositionMode_Source相反。

Ar = Ad
Cr = Cd

在这里插入图片描述

1.4.4 DestinationAtop

目标像素混合在源的顶部,目标像素的alpha减小目标像素的alpha。此模式与CompositionMode_SourceAtop相反。

Ar = As * ( 1 - Ad ) + Ad * As ) = As
Cr = Cs * ( 1 - Ad ) + Cd * As

在这里插入图片描述

1.4.5 DestinationIn

输出是目标,其中的alpha值被源的alpha值减小。此模式与CompositionMode_SourceIn相反。

Ar = Ad * As
Cr = Cd * As

位于源内部的那部分目的地取代目标。它与DstOut相反,但是,如果使用50%的alpha 值,两种操作看起来一样。

在这里插入图片描述

1.4.6 DestinationOut

输出是目标图像,其中的alpha值由源的A值而减少。此模式与SourceOut相反。

Ar = Ad * ( 1 - As )
Cr = Cd * ( 1- As )

位于源外部的那部分目的地取代目标。它与DstIn相反,但是,如果使用50%的alpha 值,两种操作看起来一样。

在这里插入图片描述

1.4.7 DestinationOver

目标的Alpha用于将其混合在源像素的顶部。此模式与SourceOver相反。

Ar = As * ( 1 - Ad ) + Ad )
Cr = Cs * ( 1 - Ad ) + Cd

目的地与源组合,然后用这个结果取代目标。位于目标之外的那部分源通常用这个合成的累加的不透明度绘制。

在这里插入图片描述

1.4.8 Source

输出是源像素。 ( 这意味着基本的复制操作,并且在源像素不透明时与SourceOver相同)。

Ar = As
Cr = Cs

源复制到目的地。目的地由源来代替。在图中,蓝色矩形(目的地)没有显示在红色 椭圆下面,因为红色椭圆(源)代替了它。
在这里插入图片描述

1.4.9 SourceAtop

源像素在目标顶部混合,源像素的alpha减少了目标像素的alpha。

Ar = As * Ad + Ad * ( 1 - As ) = Ad
Cr = Cs * Ad + Cd * ( 1 - As )

位于目标内部的那部分源与目标组合。位于目的地之外的那部分源丢弃掉。
在这里插入图片描述

1.4.10 SourceIn

输出是源,其中的alpha值将减去目标的alpha值。

Ar = As * Ad
Cr = Cs * Ad

位于目的地内部的那部分源代替目的地。位于目的地之外的那部分源丢弃掉。
在这里插入图片描述

1.4.11 SourceOut

输出是源,其中目标的alpha值减小了最终的A值。

Ar = As * ( 1 - Ad )
Cr = Cs * ( 1 - Ad )

位于目的地之外的那部分源代替目的地。位于目的地内部的那部分源丢弃掉。
在这里插入图片描述

1.4.12 Xor

​ 源( 其alpha值与目标alpha值的负数)与目标合并,该目标的alpha值与源alpha的倒数成反比。 CompositionMode_Xor与按位Xor不同。

Ar = As * ( 1 - Ad ) + Ad * (1 - As)
Cr = Cs * ( 1 - Ad ) + Cd * (1 - As)

位于目的地之外的那部分源与位于源之外的那部分目的地结合。

在这里插入图片描述

书中的12种模式已经讲完,但Qt种还有12种混合模式,以及14种二元光栅操作模式。

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值