第一:線(通過斜率 線上兩個座標確定線)
畫斜率0≤
k≤1的直線的Bresenham畫線演算法的C語言程式:
void BresenhamLine (int x0, int y0, int x1, int y1, long color)
{
int x, y, dx, dy;
float k, e;
dx = x1-x0;
dy = y1- y0;
e = -0.5;
x = x0;
y = y0;
if (dx = = 0)
{
for (i=0;i≤dy;i++)
{
DrawPixel (x, y+i, color);//畫像素(x,y+i)
}
return;
}
k = dy/dx;
for (i=0;i≤dx;i++)
{
DrawPixel (x, y, color);//畫像素(x,y)
x++;
e += k;
if (e≥0)
{
y++;
e--;
}
}
}
先考慮斜率k=dy/dx≤1的直線。2.1所示,設直線方程為,其中,k = dy/dx。 假設當前像素的x座標已經確定為xi,其y座標為yi,由於座標(xi,yi)(i=0,1,…)只能取整數,那麼下一個像素的x座標,而yi+1的座標有兩種可能:
1) 保持不變,即yi+1=yi;或者
2) y座標遞增1,即yi+1=yi+1。
令,y座標是否增加1取決於誤差項d i的值。因為直線的起始點在像素中心,所以初始誤差d0=0。x每增加1,y的值相應遞增直線的斜率值k,即。一旦di+1≥1,就把它減去1,這樣保證di+1在0~1之間。當di+1≥0.5時,直線與x=xi+1的垂線的交點最接近於當前像素(xi,yi)的右上方像素(xi+1,yi+1);而當di+1<0.5時,其交點更接近於(xi,yi)右邊的像素(xi+1,yi)。為方便計算,令e0=-0.5,e i+1=di+1-0.5,增量為k。當ei+1≥0時,取當前像素(xi,yi)的右上方像素(xi+1, yi+1);而當ei+1<0時,更接近於右方像素(xi+1,yi)。
第二 圓的基礎知識
不失一般性,假設圓的圓心位於座標原點(如果圓心不在原點,可以通過座標平移使其與原點重合),半徑為R。以原點為圓心的圓C有四條對稱軸:x=0,y=0,x=y和x=-y。若已知圓弧上一點P1=C(x, y),利用其對稱性便可以得到關於四條對稱軸的其它7個點,即:
P2=C(x,-y),
P3=C(-x, y),
P4=C(-x,-y),
P5=C(y,x),
P6=C(-y,x),
P7=C(y,-x),
P8=C(-y,-x)。
這種性質稱為八對稱性。因此,只要掃描轉換八分之一圓弧,就可以通過圓弧的八對稱性得到整個圓。
為了方便起見,考慮位於第一象限的四分之一圓弧。如果以點(0,R)為起點按順時針方向產生圓,則在第一象限內y是x的單調遞減函數。假設圓心和起點均精確地落在像素中心上。
如果已經知道圓弧上的一點(x,y),下一像素的選取有三種可能:正右方像素,右下角像素和正下方像素,分別用H,D和V表示,2.2所示。這三個像素的偏差的平方為:
。
令
,
。
如果,說明圓弧到D向像素的距離大於到H向像素的距離,因此,下一個像素應當取H向的像素(xi+1,yi);反之,下一個像素應當取D向的像素(xi+1,yi-1)。經過更進一步地分析後,可以得到:
如左 公式(2-2-1)
如左 公式(2-2-2)
我們可以按以下規則選取下一個像素作為圓弧的最佳逼近點:
- 當時,如果,則取為下一個像素點,否則取D為下一個像素點。
- 當時,如果,則取D為下一個像素點,否則取V為下一個像素點;
- 當時取D為下一個像素點。
|
為了提高計算速度,我們可以在Bresenham畫圓演算法中採用只有加、減和移位(即:乘以2)操作的遞推公式如下:
a)
b)
c)
可見,只用加、減和移位操作便完全可以實現Bresenham畫圓演算法。和改進的Bresenham畫線演算法一樣,Bresenham畫圓演算法具有很高的速度和效率,因此得到廣泛的應用。
3橢圓
畫橢圓
中心在原點、軸對齊的橢圓的非參數化方程為:
。
上式可用隱式方程表示為:
由於橢圓的對稱性,僅考慮在第一象限的橢圓弧即可。橢圓弧的法向量計算公式為:
橢圓弧上斜率為-1的點將橢圓弧分為上、下部分,見圖2.3所示。在上部分(地區2),法向量的y向分量較大,選擇像素時增量Δy比較重要;在下部分(地區1),法向量的x分量較大,選擇像素時增量Δx比較重要。下面我們分開進行討論。
在地區2,設當前位置為點,下一個可能的點是像素點H和D,這時可構造判別式:
若<0,表示像素點H和D的中點在橢圓內,這時可取H為下一個像素點;若 >=0,表示像素點H和D的中點在橢圓外,這時應當取D為下一個像素點。所以,對於在地區2的橢圓弧,我們可以按左邊的規則選取下一個像素作為橢圓弧的最佳逼近點:
畫橢圓弧的規則:
如果,則取H為下一個像素點,這時有 新的判別式為:
。
否則,取D為下一個像素點,即新的判別式為:
。
對於地區1的橢圓弧,同理可得:
如果,則選取下一個像素點的座標為新的判別式為:
。
否則,則選取下一個像素點的座標為新的判別式為:
。
對於其它三個象限的橢圓弧,可以利用對稱性得到。
其它曲線
二次曲線的一般方程為
令
,
我們可以對二次曲線進行分類:
二次曲線也可以用參數方程表示為:
如果時,則r(t)是一條拋物線;當時,r(t)是一條雙曲線;當時,r(t)是橢圓。
對於橢圓和圓弧,我們可以用前面的方法進行光柵化顯示。對於雙曲線,我們可以採用差分的方法進行點陣圖形顯示。
對於三次或三次以上的多項式曲線f(x,y)=0,可以採用遞迴空間子分演算法進行點陣圖形顯示。其基本思想是:首先建立有頂點(
,
)和(
,
)構成的包圍盒,如果曲線f(x,y)=0通過包圍盒,而且包圍盒的大小大於一個像素,則對包圍盒再進行子分,直到包圍盒只有一個像素大為止,然後用給定曲線的顏色輸出;如果曲線f(x,y)=0不通過包圍盒,則該地區用背景色顯示,並忽略處理。
地區填充
1) 多邊形
由一系列首尾相連的直線段構成的圖形稱為多邊形。如果在多邊形內任意選取不相同的兩點,其連線上的所有點均在該多邊形內,這樣的多邊形稱為凸多邊形;否則,稱為凹多邊形。
2) 種子填充演算法
種子填充演算法又稱為邊界填充演算法。其基本思想是:從多邊形地區的一個內點開始,由內向外用給定的顏色畫點直到邊界為止。如果邊界是以一種顏色指定的,則種子填充演算法可逐個像素地處理直到遇到邊界顏色為止。
種子填充演算法常用四連通域和八連通域技術進行填充操作。
從地區內任意一點出發,通過上、下、左、右四個方向到達地區內的任意像素。用這種方法填充的地區就稱為四連通域;這種填充方法稱為四向連通演算法。
從地區內任意一點出發,通過上、下、左、右、左上、左下、右上和右下八個方向到達地區內的任意像素。用這種方法填充的地區就稱為八連通域;這種填充方法稱為八向連通演算法。
一般來說,八向連通演算法可以填充四向連通地區,而四向連通演算法有時不能填充八向連通地區。例如,八向連通填充演算法能夠正確填充2.4a所示的地區的內部,而四向連通填充演算法只能完成2.4b的部分填充。
圖2.4 四向連通填充演算法
四向連通填充演算法:
a) 種子像素壓入棧中;
b) 如果棧為空白,則轉e);否則轉c);
c) 彈出一個像素,並將該像素置成填充色;並判斷該像素相鄰的四連通像素是否為邊界色或已經置成多邊形的填充色,若不是,則將該像素壓入棧;
d) 轉b);
e) 結束。
四向連通填充方法可以用遞迴函式實現如下:
演算法2.3 四向連通遞迴填充演算法:
void BoundaryFill4(int x, int y, long FilledColor, long BoundaryColor)
{
long CurrentColor;
CurrentColor = GetPixelColor(x,y);
if (CurrentColor != BoundaryColor && CurrentColor != FilledColor)
{
SetColor(FilledColor);
SetPixel (x,y);
BoundaryFill4(x+1, y, FilledColor, BoundaryColor);
BoundaryFill4(x-1, y, FilledColor, BoundaryColor);
BoundaryFill4(x, y+1, FilledColor, BoundaryColor);
BoundaryFill4(x, y-1, FilledColor, BoundaryColor);
}
}
上述演算法的優點是非常簡單,缺點是需要大量棧空間來儲存相鄰的點。一個改進的方法就是:通過沿掃描線填充水平像素段,來處理四連通或八連通相鄰點,這樣就僅僅只需要將每個水平像素段的起始位置壓入棧,而不需要將當前位置周圍尚未處理的相鄰像素都壓入棧,從而可以節省大量的棧空間。
3) 其它填充演算法
掃描線填充演算法是另一個常用的多邊形填充演算法。其基本思想是:對於一個給定的多邊形,用一組水平或垂直的掃描線進行掃描,分別求出每條掃描線與多邊形的交點,這些交點將掃描線分割為相間排列的落在多邊形內和多邊形外的線段,將落在多邊形內的所有線段上的每個像素點賦以給定的多邊形填充色。具體演算法可以參考第7章"消隱顯示"的相關內容
點陣和向量
點陣字元
在點陣字型檔中,每個字元由一個位元影像表示(2.5所示),並把它用一個稱為字元掩膜的矩陣來表示,其中的每個元素都是一位位元,如果該位為1表示字元的筆畫經過此位,該像素置為字元顏色;如果該位為0,表示字元的筆畫不經過此位,該像素置為背景顏色。點陣字元的顯示分為兩步:首先從字型檔中將它的位元影像檢索出來,然後將檢索到的位元影像寫到幀緩衝器中。
在實際應用中,同一個字元有多種字型(如宋體、楷體等),每種字型又有多種大小型號,因此字型檔的儲存空間十分龐大。為了減少儲存空間,一般採用壓縮技術。
圖2.5 字元的點陣表示和向量輪廓表示
、
2) 向量字元
向量字元記錄字元的筆畫資訊而不是整個位元影像,具有儲存空間小,美觀、變換方便等優點。例如:在AutoCAD中使用圖形實體-形(Shape)-來定義向量字元,其中,採用了直線和圓弧作為基本的筆畫來對向量字元進行描述。
對於字元的旋轉、放大、縮小等幾何變換,點陣字元需要對其位元影像中的每個象素進行變換,而向量字元則只需要對其幾何圖素進行變換就可以了,例如:對直線筆畫的兩個端點進行變換,對圓弧的起點、終點、半徑和圓心進行變換等等。
向量字元的顯示也分為兩步。首先從字型檔中將它的字元資訊。然後取出端點座標,對其進行適當的幾何變換,再根據各端點的標誌顯示出字元。
輪廓字形法是當今國際上最流行的一種字元表示方法,其壓縮比大,且能保證字元品質。輪廓字形法採用直線、B樣條/Bezier曲線的集合來描述一個字元的輪廓線。輪廓線構成一個或若干個封閉的平面地區。輪廓線定義加上一些指示橫寬、豎寬、基點、基準等等控制資訊就構成了字元的壓縮資料。
點陣圖形反走樣基礎
Bresenham直線演算法產生的直線圖形一般都呈階梯狀(見圖2.1),實際上,這是點陣圖形的一種走樣現象。這種走樣現象是由於採用離散量表示連續量引起的。通常,我們把由離散量表示連續量引起的失真稱為走樣;把減少或克服走樣效果的技術稱為反走樣技術,簡稱反走樣。
點陣圖形的走樣有如下幾種:
a) 產生階梯或鋸齒形;
b) 細節或紋理繪製失真;
c) 狹小圖形遺失;
d) 即時動畫忽隱忽現、閃爍跳躍。
當走樣嚴重時,可能導致意外的結果。例如,考慮圖2.6 a)和b)所示的訊號,它們是兩組完全不同的訊號,對它們用同一頻率進行採樣(見圖2.6中的黑點 ),重建後的訊號卻相同。圖2.6 c)或d)是圖2.6 a)訊號的走樣,也是圖2.6 b)訊號的走樣。造成走樣的原因是由於採樣頻率太低造成的欠採樣。根據採樣定理,為了避免走樣,採樣頻率至少應是訊號最高頻率的2倍。對於小於像素尺寸的繪圖物件,一方面,如果它未能覆蓋像素中用於計算其屬性的像素中點,則這個對象將不會顯示出來;另一方面,如果它覆蓋了像素中用於計算其屬性的那一點,它將不恰當地代表整個像素的屬性。圖2.7就是這樣的一個例子。當在光柵裝置上顯示圖2.7 a)所示的一組細長的多邊形時,由於僅僅當像素中心被這些矩形覆蓋時該像素才被顯示,因此造成狹小的圖形遺失、圖形細節失真,其結果2.7 b)所示。在動畫序列中,這種走樣現象會導致圖形時隱時現,產生閃爍。圖2.8是一個小卡通動畫序列中的三幅畫。如果像素的屬性由其中心決定,則在第一幀中,這個小卡通人是不可見的,在第二幀小卡通人可見,但第三幀又不可見。這樣,小卡通人給人的感覺不是在緩慢地連續前進,而是一明一暗地在閃爍。
為了提高圖形品質,必須克服或減少走樣現象。這就是本節研究的重點。
點陣圖形的反走樣方法主要有兩類:
第一類是超採樣或稱後置濾波。這類演算法的基本思想著眼於提高解析度,雖然採用高解析度的點陣圖形顯示器也是一個選擇,但它受到客觀條件的限制,而且也不經濟。因此,我們往往採用軟體實現的方法,即:將低解析度的圖形像素劃分為許多子像素,在較高解析度上對各子像素的顏色值或灰階值進行計算,然後採用某種平均演算法,將原像素內的各子像素的顏色值或灰階值的平均值作為該像素顯示的顏色值或灰階值,在較低解析度的點陣圖形裝置上進行顯示。
第二類方法稱為前置濾波。即:把像素作為一個有限地區而不是一個面積為零的點來處理。