EGE专栏:EGE专栏
光栅图也叫做位图、点阵图、像素图。图像其实由一个个像素点组成,每个像素点都有自己的颜色。显示屏就是由一个个的小点组成,当这些点都很小,而且都很紧密地排在一起时。我们就看到一幅连续的图像。但靠近看时,还是能看到一一的小点。
输出时,是读取显存中的每一个点的颜色数据,然后根据颜色输出显示屏上。
点的颜色是用无符号整型表示的,绘图时就是在改变这些颜色数据。既然是改变颜色数据,那就有原有数据和新数据,所以改变就有很多种操作方式,一般是位操作。可以是直接赋值,也可以是保留原有数据,或者两个数值位与,位或,取反等。
二元光栅操作码就是用来控制改变像素时的位操作方式的。每个二元光栅操作码对应一种位操作方式。
设置位操作方式的函数为setwritemode
, 函数声明:
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() 设置的用于当前绘制或填充的颜色。当我们在上面绘制时,就根据这两个颜色和位操作模式计算得出最终的颜色。 后面还有个三元光栅操作码,是用于图像处理的。
下面做个二元光栅操作码的示例:
#include
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, 代表做位或操作,就得到了一个不同的颜色。
三元光栅操作码,是设置两个图像绘图时对像素的一种位操作方式。两个图像图像对应点上的两个像素颜色加上当前填充颜色一共三个,所以是三元。
三元光栅操作码其实是在绘制图像时设置的,来看看putimage() 的声明:
void EGEAPI putimage(int dstX, int dstY, const PIMAGE pSrcImg, DWORD dwRop = SRCCOPY);
最后面有个 DWORD 类型的参数 dwRop, 那个就是三元光栅操作码,默认是SRCCOPY,也就是直接用源图像覆盖原有图像。
三元光栅操作码有非常多,这里就不多说,可以查看官网说明
三元光栅操作码 http://xege.org/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);
如果在右边找到名字,可以直接使用名字,但这个没有。
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);
putimage(0, 0, mask, 0x00220326);
PIMAGE temp = newimage(320, 320);
//先把原图复制到临时图像上
putimage(temp, 0, 0, pimg);
//做与操作
putimage(temp, 0, 0, mask, SRCAND);
这样我们就把中间的保留了,外面都变成了纯黑。因为纯黑就是0x000000, 所以接下来做或操作时,就相当于没有。
putimage(0, 0, temp, SRCPAINT);
这样就成功了,别忘了,图像不用后要记得使用delimage()
delimage(temp);
delimage(mask);
delimage(pimg);
代码如下:
因为不知怎么回事,我测试的缩放putimage(),三元操作码失效,所以这个函数没写成缩放型
#include
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);
}
EGE专栏:EGE专栏