分享

(十)EGE光栅操作

 我就是个蒟蒻 2020-08-09

光栅操作

(1) 光栅

  光栅图也叫做位图、点阵图、像素图。图像其实由一个个像素点组成,每个像素点都有自己的颜色。显示屏就是由一个个的小点组成,当这些点都很小,而且都很紧密地排在一起时。我们就看到一幅连续的图像。但靠近看时,还是能看到一一的小点。
  输出时,是读取显存中的每一个点的颜色数据,然后根据颜色输出显示屏上。
  点的颜色是用无符号整型表示的,绘图时就是在改变这些颜色数据。既然是改变颜色数据,那就有原有数据和新数据,所以改变就有很多种操作方式,一般是位操作。可以是直接赋值,也可以是保留原有数据,或者两个数值位与,位或,取反等。

(2)二元光栅操作码

  • 二元光栅操作码就是用来控制改变像素时的位操作方式的。每个二元光栅操作码对应一种位操作方式
  • 设置位操作方式的函数是
void setwritemode(
    int mode.
    PIMAGE pimg = NULL
);

其中的mode就是二元光栅操作码, pimg是要设置的图像,为NULL时就是指的是窗口,当在pimg的图像上绘图时,就是根据二元光栅码表示的位操作模式来改变颜色。

  • EGE支持全部的16种操作码, 罗列如下
R2_BLACK			绘制出的像素颜色 = 黑色
R2_COPYPEN			绘制出的像素颜色 = 当前颜色(默认)
R2_MASKNOTPEN		绘制出的像素颜色 = 原有颜色 AND (NOT 当前颜色)
R2_MASKPEN			绘制出的像素颜色 = 原有颜色 AND 当前颜色
R2_MASKPENNOT		绘制出的像素颜色 = (NOT 原有颜色) AND 当前颜色
R2_MERGENOTPEN		绘制出的像素颜色 = 原有颜色 OR (NOT 当前颜色)
R2_MERGEPEN			绘制出的像素颜色 = 原有颜色 OR 当前颜色
R2_MERGEPENNOT		绘制出的像素颜色 = (NOT 原有颜色) OR 当前颜色
R2_NOP				绘制出的像素颜色 = 原有颜色
R2_NOT				绘制出的像素颜色 = NOT 原有颜色
R2_NOTCOPYPEN		绘制出的像素颜色 = NOT 当前颜色
R2_NOTMASKPEN		绘制出的像素颜色 = NOT (原有颜色 AND 当前颜色)
R2_NOTMERGEPEN		绘制出的像素颜色 = NOT (原有颜色 OR 当前颜色)
R2_NOTXORPEN		绘制出的像素颜色 = NOT (原有颜色 XOR 当前颜色)
R2_WHITE			绘制出的像素颜色 = 白色
R2_XORPEN			绘制出的像素颜色 = 原有颜色 XOR 当前颜色

  所以二元操作码中的二元指的就是图像原来的颜色当前颜色
  "当前颜色"是指通过 setcolor() 或 setfillcolor() 设置的用于当前绘制或填充的颜色。当我们在上面绘制时,就根据这两个颜色和位操作模式计算得出最终的颜色。 后面还有个三元光栅操作码,是用于图像处理的。

  • 可以看到,默认的二元光栅操作码R2_COPYPEN, 就是直接用当前颜色来作为最终颜色,这也是我们熟悉的一种方式,也有不管两个是什么颜色,画上去都是黑色,或者都是白色。
  • 更多的是对两个颜色进行 与、或、非、取反、异或 的位操作,例如,R2_MERGEPEN, 就是将两个颜色进行或运算。红色是0xFF0000, 蓝色是0x0000FF, 或运算之后,得到紫色0xFF00FF.

下面做个二元光栅操作码的示例:

#include <graphics.h>

int main()
{
	initgraph(640, 320, 0);
	
	setbkcolor(0x00FF00);
	setcolor(0xFF0000);
	setfillcolor(0X0000FF);
	
	fillellipse(160, 160, 160, 160);
	
	//颜色做位或
	setwritemode(R2_MERGEPEN);
	fillellipse(480, 160, 160, 160);
	
	//改回原来的模式
	setwritemode(R2_COPYPEN);
	
	getch();

	closegraph();
	return 0;
}

可以看到修改位操作模式, R2_MERGEPEN, 代表做位或操作,就得到了一个不同的颜色。
在这里插入图片描述

(3) 三元光栅操作码

  三元光栅操作码,是设置两个图像绘图时对像素的一种位操作方式。两个图像图像对应点上的两个像素颜色加上当前填充颜色一共三个,所以是三元。
三元光栅操作码其实是在绘制图像时设置的,来看看putimage() 的声明:

void EGEAPI putimage(int dstX, int dstY, const PIMAGE pSrcImg, DWORD dwRop = SRCCOPY);   
  • 最后面有个 DWORD 类型的参数 dwRop, 那个就是三元光栅操作码,默认是SRCCOPY,也就是直接用源图像覆盖原有图像。

  • 三元光栅操作码有非常多,这里就不多说,可以查看官网说明
    三元光栅操作码 http:///manual/api/img/rop.htm

  • 三元光栅操作码太多,所以用逆波兰表达式来表示操作名,也就是后缀表达式,平时我们使用的是中缀表达式,例如:中缀表达式的1 + 2,对应的后缀表达式就是 1 2 +, 把运算符放在后面,再如 1 + 2 * 3 ==> 1 + (2 * 3)⇒ 1 + (2 3 *) ⇒ 1 2 3 * +, 就是这么转换来的,看原来是怎么计算的,把那部分的运算符放在后面作为结果,当作一个整体再继续计算, 上面的那个先算 2 * 3, 本来应该得6,但是我们是要转成后缀表达式,所以结果是 (2 3 *), 把 (2 3 *)作为一个整体 在计算 1 + (2 3 *),那自然就是 1 (2 3 *) +了,因为后缀表达式运算符顺序就代表计算顺序,不需要括号来说明运算顺序,所以得到结果是 1 2 3 * +,按照这个我们可以根据我们需要的运算, 转成后缀表达式,再去资料里查对应的码值。

  • 开始计算后缀表达式了,用一些大写字母代表颜色

  • 在这里插入图片描述
    用一些小写字母代表运算
    在这里插入图片描述
    觉得好我们要的操作后,转换成后缀表达式,到红色框圈出的一栏找, 找到后看蓝色一栏对应的码值是什么,是十六进制表示的,就可以拿来用了,用的时候别忘了 前面加个0x .右边紫色圈出的地方是一部分已经定义好的宏,记住后下次直接输入那个名称就行。
    在这里插入图片描述
    后缀表达式示例,假如我们要对源图像颜色取反,再和目标图像颜色位与操作, 那就是 D and (not S), 转成后缀,那就是D (S not) and, 按照上面表示那就是 D S n a, 所以在上面找到是在第22号那一列, 对应值是 00220326, 那么就是 0x00220326, 设置如下:

putimage(0, 0, pimg, 0x00220326);

如果在右边找到名字,可以直接使用名字,但这个没有。

三元光栅操作符的例子,抠图:
在这里插入图片描述
在这里插入图片描述

  • 先将一张图片准备好,然后把一张掩膜图准备好,这里我们的图像和掩膜要一样大的(我这里是320x320) 掩膜就是把想要的部分填充上纯白色 0xFFFFFF, 把不想要的部分填充成纯黑 0x000000. 这个可以先创建一个空图像,设置好颜色后,在上面填充纯白,背景是纯黑。我们要在上面画四个圆,因为普通绘图函数的填充函数是带边框的,所以不仅填充颜色要设置成纯白,前景颜色也要纯白,背景颜色一开始其实就是纯黑,不设置也行。

	PIMAGE mask = newimage(320, 320);
	setbkcolor(BLACK, mask);

	//颜色都是纯白
	setcolor(0XFFFFFF, mask);
	setfillcolor(0xFFFFFF, mask);

	fillellipse(100, 160, 100,100, mask);

	fillellipse(220, 160, 100, 100, mask);

	fillellipse(160, 220, 100, 100, mask);

	fillellipse(160, 100, 100, 100, mask);

在这里插入图片描述

  • 然后就是操作了,接下来是要把绘制的区域中间扣个洞(就是把我们要画的地方涂上纯黑),比如,原来窗口是黄色的,那就把中间弄黑, 保留掩膜的黑色部分, 操作是 屏幕颜色 and (not 源图像颜色), 即刚才说的 DSna
putimage(0, 0, mask, 0x00220326);

在这里插入图片描述

  • 然后我们再把我们要扣的图像留下中间部分, 操作是 目标图像 位与 源图像, 即 DSa, 名称是 SRCAND, 为了保留原来的图像,我们还是另外创建一个图像来保存吧。
PIMAGE temp = newimage(320, 320);
	//先把原图复制到临时图像上
	putimage(temp, 0, 0, pimg);
	//做与操作
	putimage(temp, 0, 0, mask, SRCAND);

这样我们就把中间的保留了,外面都变成了纯黑。因为纯黑就是0x000000, 所以接下来做或操作时,就相当于没有。
在这里插入图片描述

  • 再然后将我们抠出来的图片与目标区域进行位或操作, 或操作三元光栅操作码名称是SRCPAINT,
putimage(0, 0, temp, SRCPAINT);

在这里插入图片描述+ 在这里插入图片描述= 在这里插入图片描述
这样就成功了,别忘了,图像不用后要记得使用delimage()

delimage(temp);
delimage(mask);
delimage(pimg);

代码如下:
因为不知怎么回事,我测试的缩放putimage(),三元操作码失效,所以这个函数没写成缩放型

#include <graphics.h>

void cutoutImage(PIMAGE dest, int x, int y, int w, int h, PIMAGE src, PIMAGE mask);

//使用之前编写的从文件缩放加载图像函数
int getimage_zoom(PIMAGE& pDstImg, LPCSTR  pImgFile, int zoomWidth, int zoomHeight);
int main()
{
	initgraph(320, 320, 0);
	setbkcolor(YELLOW);
	setcolor(0xFF0000);
	setfillcolor(0X0000FF);

	int width = 320, height = 320;
	
	PIMAGE pimg = newimage();
	getimage_zoom(pimg, "girl.jpg", width, height);

	PIMAGE mask = newimage(width, height);
	setbkcolor(BLACK, mask);

	//颜色都是纯白
	setcolor(0XFFFFFF, mask);
	setfillcolor(0xFFFFFF, mask);

	fillellipse(100, 160, 100,100, mask);
	fillellipse(220, 160, 100, 100, mask);
	fillellipse(160, 220, 100, 100, mask);
	fillellipse(160, 100, 100, 100, mask);

	cutoutImage(NULL, 0, 0, pimg, mask);

	delimage(mask);
	delimage(pimg);

	getch();

	closegraph();
	return 0;
}

//原图src, 和掩膜mask大小要一样, x, y, w, h, 为在deset上绘制的区域
//因为不知怎么回事,我测试的缩放putimage(),三元操作码失效,所以这个函数没写成缩放型
void cutoutImage(PIMAGE dest, int x, int y, PIMAGE src, PIMAGE mask)
{
	int width = getwidth(src), height = getheight(src);
	putimage(dest,  0, 0, mask, 0x00220326);

	PIMAGE temp = newimage(width, height);
	putimage(temp, 0, 0, src);

	putimage(temp, 0, 0, mask, SRCAND);

	putimage(dest, 0, 0, temp, SRCPAINT);

	delimage(temp);
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多