畫線函數Glib_Line算法的研究
在這裏首先先簡單把我對函數的功能的理解闡述一下,方便後面的分析:Glib_Line函數實現的功能是通過參數給定(x1,y1,x2,y2,color),來確定起點(x1,y1)和終點(x2,y2)兩點之間的一條直線,並通過color參數來確定這條直線的顏色。這裏這條語句的算法重點在於如何給像素點填充對應的顏色來畫出任意直線,至於顏色具體值的確定會在後續的配色原理中闡述。首先先來看兩幅圖片:
他們是相同的一個圖片,圖片2是圖片1放大後的情況。
我們平時在LCD上畫直線還無所謂,但是如果畫斜線,由於LCD上沒有半個像素點,所以,當x或y增加一個像素點之後,對應的y或x要麽增加一個像素點,要麽保持原來的像素點不動,舉個例子:假設:x1=1,x2=320,y1=1,y2=2,那麽畫線後的情況是x軸的前面160個像素點,y軸的是1,在x軸的後面160個像素點,y軸是2。
我們根據圖3來仔細分析畫斜線時可能出現的問題,以及處理的算法。這裏我們取眾多情況中的一種來分析,即dx>=0 ,dy >= 0,dx>=dy
我們把像素點放大抽象為圖3,其中黑點定為起始點,灰色線所指的點為第二個像素點的理論位置,綠色點和黃色點為實際像素點可能賦值的點,並且我們這裏規定,綠色點到灰色點的距離為a,黃色點到灰色點的距離為b。灰色點坐標為(x,y),綠色點坐標為(x1+1,y1+1),黃色點坐標為(x1+1,y1)。並且規定所畫直線在x軸上的投影為dx=x2-x1,在y軸上的投影為dy=y2-y1。
理論上,灰色點才是理論點,但是由於像素點不能有小數,所以只可能是上面的綠點或者下面的黃點選一個,具體選哪個要由誰更靠近灰點(誤差小)決定
這裏我們根據數學知識可以得到:
a=(y+1)-(x+1)×(dy/dx)
b=(x+1)×(dy/dx)- y
下面我們只需要判斷a和b誰大誰小便可以決定到底選黃點還是綠點。
這裏我們選用差值法來判斷a,b的大小:
為了去掉小數點,因為dx>0,所以有d=dx*(b-a)= 2*(xdy-ydx)+2dy-dx;
接下來我們只需要判斷dx*(b-a)>0還是dx*(b-a)<0;
這個時候我們用相對坐標來考慮,把起始點(x1,y1)平移到(0,0)點,整條直線雖然也隨之平移,這個時候黃點和綠點的坐標也隨之改變,但是不改變第二個點到底是在綠點還是黃點的位置。同理也不改變後面其他像素點的選擇。所以采用相對坐標後x1=0,y1=0, d1=dx*(b-a)=2dy-dx
下面我們就對照著函數代碼來分析程序中算法的實現。同樣我們取眾多情況中的一種來分析,即dx>=0 ,dy >= 0,dx>=dy的情況,和上面算法對應起來.
1 e=dy-dx/2; 2 3 while(x1<=x2) 4 5 { 6 7 PutPixel(x1,y1,color); 8 9 if(e>0){y1+=1;e-=dx;} 10 11 x1+=1; 12 13 e+=dy; 14 15 }
這段程序就是在滿足dx>=0 ,dy >= 0,dx>=dy的情況下,需要執行的內容。
首先是把和a,b差值有直接聯系的e求出來,接下來便進入while循環, while循環的作用是對從x1到x2之間所有的像素點賦值。
因為dx>dy,結合圖3我們知道在x1和x2之間對應要賦值的有x2-x1個像素點,所以這就是while語句括號中判斷的作用,就是對x1和x2之間的x2-x1個像素點賦值,因此每次x1+=1語句的作用就是對x2-x1這個數進行計數,每次加1直到等於x2為止,等於x2了就算把所有該賦值的像素點都賦值了。
while語句裏,先是對(x1,y1)這個位置的像素點賦值,然後如果橫坐標沒到終點的橫坐標x2,就一直賦值,期間要對下一個點的位置做判斷,即if(e>0)語句:
1.第一次判斷如果b>a,即b-a>0,也就e>0,這個時候我們肯定是選擇綠點,所以第二點的坐標為(x1+1,y1+1),x1+1在整個while中的x1+=1中實現,y1+1在if語句中的y1+=1中實現,然後便是改變判斷第三點的位置時要用到的e。
因為d2=2*(x2dy-y2dx)+2dy-dx,因為第一點相對坐標為(0,0),則第二點的相對坐標為(1,1),所以d2=4dy-3dx,此時的e2=d2/2=2dy-3/2dx=e+dy-dx;e2中對原來的e+dy是在整個while中的e+=dy中實現的,對原來的e-dx是在在if語句中的e-=dx中實現的。
2.第一次判斷如果b<a,即b-a<0,也就是e<0,這個時候我們肯定選擇黃點,所以第二點的坐標為(x1+1,y1),x1+1在整個while中的x1+=1中實現,此時不進入if語句,然後便是改變判斷第三點的位置時要用到的e。
因為d2=2*(x2dy-y2dx)+2dy-dx,因為第一點相對坐標為(0,0),則第二點的相對坐標為(1,0),所以d2=4dy-dx,此時的e2=d2/2=2dy-dx/2=e+dy;e2中對原來的e+dy是在整個while中的e+=dy中實現的,此時不進入if語句,dx不增加。
同樣的道理,到了判斷e的時候,重復上面的過程,d=2*(xdy-ydx)+2dy-dx;x,y分別為當時判斷點的相對坐標。
例如,第二點選擇的是黃點,第三點選擇的是綠點,通過算法我們得到e2=e1+dy;e3=e2+dy-dx=e1+2dy-dx=e+2dy-dx=3dy-3/2dx;
下面通過對e的定義來驗證一下:
第二點的相對坐標為(1,0),則第三點的坐標為(2,1)
e3=d3/2=(x3dy-y3dx)+dy-dx/2=2dy-dx+dy-dx/2=3dy-3/2dx;
兩種方法的e3值完全一樣,以此類推證明了程序與算法的吻合,程序完全可以實現算法所需要的功能。
以上只是分析了其中dx>=0 ,dy >= 0,dx>=dy一種情況,我們仔細分析發現,其他的七種情況的數學含義以及分析方法和第一種完全一樣,只是斜線的斜率,以及dx,dy長短等不同而以,這裏不做過多的贅述。
所有的情況分別為8種:
1.dx>=0,dy>0 ,dx>=dy
2.dx>=0,dy>0 ,dx<dy
3.dx>=0,dy<0 ,dx>=dy
4.dx>=0,dy<0 ,dx<dy
5.dx<0, dy>0 ,dx>=dy
6.dx<0, dy>0 ,dx<dy
7.dx<0, dy<0 ,dx>=dy
8.dx<0, dy<0 ,dx<dy
具體代碼如下:
1 // glib庫中的畫線函數,可以畫斜線,線兩端分別是(x1, y1)和(x2, y2) 2 void glib_line(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int color) 3 { 4 int dx,dy,e; 5 dx=x2-x1; 6 dy=y2-y1; 7 8 if(dx>=0) 9 { 10 if(dy >= 0) // dy>=0 11 { 12 if(dx>=dy) // 1/8 octant 13 { 14 e=dy-dx/2; 15 while(x1<=x2) 16 { 17 lcd_draw_pixel(x1,y1,color); 18 if(e>0){y1+=1;e-=dx;} 19 x1+=1; 20 e+=dy; 21 } 22 } 23 else // 2/8 octant 24 { 25 e=dx-dy/2; 26 while(y1<=y2) 27 { 28 lcd_draw_pixel(x1,y1,color); 29 if(e>0){x1+=1;e-=dy;} 30 y1+=1; 31 e+=dx; 32 } 33 } 34 } 35 else // dy<0 36 { 37 dy=-dy; // dy=abs(dy) 38 39 if(dx>=dy) // 8/8 octant 40 { 41 e=dy-dx/2; 42 while(x1<=x2) 43 { 44 lcd_draw_pixel(x1,y1,color); 45 if(e>0){y1-=1;e-=dx;} 46 x1+=1; 47 e+=dy; 48 } 49 } 50 else // 7/8 octant 51 { 52 e=dx-dy/2; 53 while(y1>=y2) 54 { 55 lcd_draw_pixel(x1,y1,color); 56 if(e>0){x1+=1;e-=dy;} 57 y1-=1; 58 e+=dx; 59 } 60 } 61 } 62 } 63 else //dx<0 64 { 65 dx=-dx; //dx=abs(dx) 66 if(dy >= 0) // dy>=0 67 { 68 if(dx>=dy) // 4/8 octant 69 { 70 e=dy-dx/2; 71 while(x1>=x2) 72 { 73 lcd_draw_pixel(x1,y1,color); 74 if(e>0){y1+=1;e-=dx;} 75 x1-=1; 76 e+=dy; 77 } 78 } 79 else // 3/8 octant 80 { 81 e=dx-dy/2; 82 while(y1<=y2) 83 { 84 lcd_draw_pixel(x1,y1,color); 85 if(e>0){x1-=1;e-=dy;} 86 y1+=1; 87 e+=dx; 88 } 89 } 90 } 91 else // dy<0 92 { 93 dy=-dy; // dy=abs(dy) 94 95 if(dx>=dy) // 5/8 octant 96 { 97 e=dy-dx/2; 98 while(x1>=x2) 99 { 100 lcd_draw_pixel(x1,y1,color); 101 if(e>0){y1-=1;e-=dx;} 102 x1-=1; 103 e+=dy; 104 } 105 } 106 else // 6/8 octant 107 { 108 e=dx-dy/2; 109 while(y1>=y2) 110 { 111 lcd_draw_pixel(x1,y1,color); 112 if(e>0){x1-=1;e-=dy;} 113 y1-=1; 114 e+=dx; 115 } 116 } 117 } 118 } 119 }
本文轉自:http://blog.chinaunix.net/uid-26435987-id-3077711.html
畫線函數Glib_Line算法的研究