- 使用三元光栅操作实现透明贴图
基本概念
“三元光栅操作”是指源图像与目标图像的位合并操作。
操作对象涉及三个:源图像、目标图像、当前填充颜色(注:透明贴图不使用“当前填充颜色”)。
位操作包括:AND、NOT、OR、XOR。
全部的三元光栅操作码请参考 EasyX 在线帮助:https://docs.easyx.cn/ternary-raster-operations
例如,三元光栅操作码“PATPAINT”,查表得对应的布尔功能为“DPSnoo”(逆波兰表示法,其中 D、S、P 分别表示目标图像、源图像、当前填充颜色),该表达式展开后为:D or (P or (not S)),表示先将源图像按位取反,再与当前填充颜色执行 OR 操作,在与目标图像执行 OR 操作。这就是 putimage 函数以 PATPAINT 参数执行后的显示结果。
一种基于三元光栅操作的透明贴图法
首先准备图片:
原图:需要透明的部分,用纯黑色表示。
掩码图:与原图对应。原图需要透明的部分,用纯黑色表示;原图需要显示的部分,用纯白色表示。
目标图:通常就是屏幕,不用担心屏幕显示什么。
注意:准备的图片与后面的代码是配套的。例如,原图 src3.gif 和掩码图 mask3.gif 处理成这样:
原图:
掩码图:
然后用以下代码实现贴图:
#include <graphics.h> // EasyX_20190219(beta)
#include <conio.h>
// 透明贴图函数
// 参数:
// x, y: 目标贴图位置
// srcimg: 源 IMAGE 对象指针。NULL 表示默认窗体
// maskimg:掩码 IMAGE
void transparentimage(int x, int y, IMAGE *srcimg, IMAGE *maskimg)
{
putimage(x, y, maskimg, SRCAND);
putimage(x, y, srcimg, SRCPAINT);
}
// 主函数
int main()
{
initgraph(600, 400); // 初始化图形窗口
IMAGE src, mask;
loadimage(&src, _T("D:\\src3.gif"));
loadimage(&mask, _T("D:\\mask3.gif"));
// 画个简单背景
setlinecolor(GREEN);
for (int y = 0; y < 480; y += 3)
line(0, y, 639, y);
// 普通贴图
putimage(0, 0, &src);
// 透明贴图
transparentimage(120, 0, &src, &mask);
// 按任意键退出
_getch();
closegraph();
return 0;
}
以上代码的执行效果如下:
原理讲解
现在用一维数字的形式来讲解光栅操作的原理。假设:
源 图:00 00 00 56 78 9a bc 00 (00 表示透明的部分(对应源图黑色部分(这个说法其实也不大对,因为羊的脚啊,嘴啊也是黑色的,但我们不需要其透明,
说到底还是该说成“要透明的部分”),其它数字表示显示的部分(对应源图需要显示的部分))
掩码图:ff ff ff 00 00 00 00 ff (ff 表示透明的部分(对应掩码图白色部分),00 表示显示的部分(对应于掩码图黑色部分)
目标图:12 34 12 34 12 34 12 34
执行步骤:
初始目标图:12 34 12 34 12 34 12 34
执行:putimage(x, y, 掩码图, SRCAND);
/* SRCAND 表示“掩码图(对于函数putimage来说掩码图就是源图像)
AND 目标图(对于函数putimage来说掩码图就是目标图像,也就是画布)*/
目标图变为:12 34 12 00 00 00 00 34
/*到这里简而言之就是发生了这样一件事:目标图像(画布)与掩码图(源图像)相与
而“与”运算是0&1=0,0&0=0,1&1=1,方便理解可以认为两个十六进制位的00就代表黑色(但这个说法超不严谨,懂的人麻烦评论区解释一下),
结果便是,掩码图的黑色部分覆盖了目标图像(画布),而由于FF等于全是1,与的结果便是维持目标图像原样,
于是目标图像 = 目标图像 AND 源图像,
即目标图像中只有掩码图黑色部分覆盖了目标图像*/
执行:putimage(x, y, 源图, SRCPAINT); // SRCPAINT 表示“源图(源图像) OR 目标图(对于函数putimage来说掩码图就是目标图像,也就是画布)”
/*这里由于上一步掩码图把要显示的区域覆盖成了黑色即全是0,
那么通过或运算,源图要显示的部分就会覆盖目标图像,
而不需要显示的部分由于是黑色,即FF等于全是1,或运算的特性导致其不会影响目标图像
于是便实现了源图只覆盖了目标图像中上一次操作后掩码图用黑色覆盖了的部分*/
目标图变为:12 34 12 56 78 9a bc 34
根据以上原理可知:通过三元光栅操作实现透明贴图的办法有很多种,可以根据自己的源图、掩码图的状态写对应的代码。在前面的例子中,源图的透明部分是纯黑色,掩码图用纯白色表示透明的部分、纯黑色表示显示的部分。
这里再举一个不同的例子,在这个例子中,源图的透明部分用的是纯白色,掩码图用纯黑色表示透明的部分、纯白色表示显示的部分,如下:
源 图:ff ff ff 56 78 9a bc ff (ff 表示透明的部分,其它数字表示显示的部分)
掩码图:00 00 00 ff ff ff ff 00 (00 表示透明的部分,ff 表示显示的部分)
目标图:12 34 12 34 12 34 12 34
对应的执行步骤为:
初始目标图:12 34 12 34 12 34 12 34
执行:putimage(x, y, 掩码图, NOTSRCERASE); // NOTSRCERASE 表示“NOT(掩码图 OR 目标图)”
目标图变为:ed cb ed 00 00 00 00 cb
执行:putimage(x, y, 源图, SRCINVERT); // SRCINVERT 表示“源图 XOR 目标图”
目标图变为:12 34 12 56 78 9a bc 34