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, 代表做位或操作,就得到了一個不同的顔色。
(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 * +,按照這個我們可以根據我們需要的運算, 轉成字尾表達式,再去資料裡查對應的碼值。
- 開始計算字尾表達式了,用一些大寫字母代表顔色
- 用一些小寫字母代表運算 覺得好我們要的操作後,轉換成字尾表達式,到紅色框圈出的一欄找, 找到後看藍色一欄對應的碼值是什麼,是十六進制表示的,就可以拿來用了,用的時候别忘了 前面加個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(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);
}