天天看點

EGE基礎:光栅操作篇

EGE專欄:EGE專欄

目錄

  • 光栅操作
    • (1) 光栅
    • (2)二進制光栅操作碼
    • (3) 三元光栅操作碼

光栅操作

(1) 光栅

  光栅圖也叫做位圖、點陣圖、像素圖。圖像其實由一個個像素點組成,每個像素點都有自己的顔色。顯示屏就是由一個個的小點組成,當這些點都很小,而且都很緊密地排在一起時。我們就看到一幅連續的圖像。但靠近看時,還是能看到一一的小點。

  輸出時,是讀取顯存中的每一個點的顔色資料,然後根據顔色輸出顯示屏上。

  點的顔色是用無符号整型表示的,繪圖時就是在改變這些顔色資料。既然是改變顔色資料,那就有原有資料和新資料,是以改變就有很多種操作方式,一般是位操作。可以是直接指派,也可以是保留原有資料,或者兩個數值位與,位或,取反等。

(2)二進制光栅操作碼

二進制光栅操作碼就是用來控制改變像素時的位操作方式的。每個二進制光栅操作碼對應一種位操作方式。

設定位操作方式的函數為

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() 設定的用于目前繪制或填充的顔色。當我們在上面繪制時,就根據這兩個顔色和位操作模式計算得出最終的顔色。 後面還有個三元光栅操作碼,是用于圖像處理的。

  • 可以看到,預設的二進制光栅操作碼是 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, 代表做位或操作,就得到了一個不同的顔色。

EGE基礎:光栅操作篇

(3) 三元光栅操作碼

  三元光栅操作碼,是設定兩個圖像繪圖時對像素的一種位操作方式。兩個圖像圖像對應點上的兩個像素顔色加上目前填充顔色一共三個,是以是三元。

三元光栅操作碼其實是在繪制圖像時設定的,來看看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 * +,按照這個我們可以根據我們需要的運算, 轉成字尾表達式,再去資料裡查對應的碼值。
  • 開始計算字尾表達式了,用一些大寫字母代表顔色
  • EGE基礎:光栅操作篇
    用一些小寫字母代表運算
    EGE基礎:光栅操作篇
    覺得好我們要的操作後,轉換成字尾表達式,到紅色框圈出的一欄找, 找到後看藍色一欄對應的碼值是什麼,是十六進制表示的,就可以拿來用了,用的時候别忘了 前面加個0x .右邊紫色圈出的地方是一部分已經定義好的宏,記住後下次直接輸入那個名稱就行。
    EGE基礎:光栅操作篇
    字尾表達式示例,假如我們要對源圖像顔色取反,再和目标圖像顔色位與操作, 那就是 D and (not S), 轉成字尾,那就是D (S not) and, 按照上面表示那就是 D S n a, 是以在上面找到是在第22号那一列, 對應值是 00220326, 那麼就是 0x00220326, 設定如下:
putimage(0, 0, pimg, 0x00220326);
           

如果在右邊找到名字,可以直接使用名字,但這個沒有。

三元光栅操作符的例子,摳圖:

EGE基礎:光栅操作篇
EGE基礎:光栅操作篇
  • 先将一張圖檔準備好,然後把一張掩膜圖準備好,這裡我們的圖像和掩膜要一樣大的(我這裡是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);
           
EGE基礎:光栅操作篇
  • 然後就是操作了,接下來是要把繪制的區域中間扣個洞(就是把我們要畫的地方塗上純黑),比如,原來視窗是黃色的,那就把中間弄黑, 保留掩膜的黑色部分, 操作是 螢幕顔色 and (not 源圖像顔色), 即剛才說的 DSna
putimage(0, 0, mask, 0x00220326);
           
EGE基礎:光栅操作篇
  • 然後我們再把我們要扣的圖像留下中間部分, 操作是 目标圖像 位與 源圖像, 即 DSa, 名稱是 SRCAND, 為了保留原來的圖像,我們還是另外建立一個圖像來儲存吧。
PIMAGE temp = newimage(320, 320);
	//先把原圖複制到臨時圖像上
	putimage(temp, 0, 0, pimg);
	//做與操作
	putimage(temp, 0, 0, mask, SRCAND);
           

這樣我們就把中間的保留了,外面都變成了純黑。因為純黑就是0x000000, 是以接下來做或操作時,就相當于沒有。

EGE基礎:光栅操作篇
  • 再然後将我們摳出來的圖檔與目标區域進行位或操作, 或操作三元光栅操作碼名稱是SRCPAINT,
putimage(0, 0, temp, SRCPAINT);
           
delimage(temp);
delimage(mask);
delimage(pimg);
           
#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);
}