1. 程式人生 > >lcd彩屏畫點畫線和畫圓 嵌入式開發

lcd彩屏畫點畫線和畫圓 嵌入式開發

在lcd顯示屏上的指定點畫出直線,點,圓

本文源於

海之遙的部落格

http://blog.sina.com.cn/atpx1989            親測可行,覺得很有意義,拿過來以備以後用

 //-----------畫點函式。引數:座標,顏色-------------------
void draw_point(int x, int y, int clor)
{
    fb[y*480 + x] = clor;
}

//-------------畫圓函式。引數:圓心,半徑,顏色----------
//        畫1/8圓 然後其他7/8對稱畫
//          ---------------->X
//          |(0,0)   0
//          |     7     1
//          |    6       2
//          |     5     3
//       (Y)V        4
//
//      L = x^2 + y^2 - r^2
void draw_circle(int x, int y, int r, int color)
{
    int a, b, num;
    a = 0;
    b = r;
    while(2 * b * b >= r * r)          // 1/8圓即可
    {
        draw_point(x + a, y - b,color); // 0~1
        draw_point(x - a, y - b,color); // 0~7
        draw_point(x - a, y + b,color); // 4~5
        draw_point(x + a, y + b,color); // 4~3

        draw_point(x + b, y + a,color); // 2~3
        draw_point(x + b, y - a,color); // 2~1
        draw_point(x - b, y - a,color); // 6~7
        draw_point(x - b, y + a,color); // 6~5
        
        a++;
        num = (a * a + b * b) - r*r;
        if(num > 0)
        {
            b--;
            a--;
        }
    }
}

//-----------畫線。引數:起始座標,終點座標,顏色--------
void draw_line(int x1,int y1,int x2,int y2,int color)
{
    int dx,dy,e;
    dx=x2-x1; 
    dy=y2-y1;
    if(dx>=0)
    {
        if(dy >= 0) // dy>=0
        {
            if(dx>=dy) // 1/8 octant
            {
                e=dy-dx/2;
                while(x1<=x2)
                {
                    draw_point(x1,y1,color);
                    if(e>0){y1+=1;e-=dx;}   
                    x1+=1;
                    e+=dy;
                }
            }
            else        // 2/8 octant
            {
                e=dx-dy/2;
                while(y1<=y2)
                {
                    draw_point(x1,y1,color);
                    if(e>0){x1+=1;e-=dy;}   
                    y1+=1;
                    e+=dx;
                }
            }
        }
        else           // dy<0
        {
            dy=-dy;   // dy=abs(dy)
            if(dx>=dy) // 8/8 octant
            {
                e=dy-dx/2;
                while(x1<=x2)
                {
                    draw_point(x1,y1,color);
                    if(e>0){y1-=1;e-=dx;}   
                    x1+=1;
                    e+=dy;
                }
            }
            else        // 7/8 octant
            {
                e=dx-dy/2;
                while(y1>=y2)
                {
                    draw_point(x1,y1,color);
                    if(e>0){x1+=1;e-=dy;}   
                    y1-=1;
                    e+=dx;
                }
            }
        }   
    }
    else //dx<0
    {
        dx=-dx;     //dx=abs(dx)
        if(dy >= 0) // dy>=0
        {
            if(dx>=dy) // 4/8 octant
            {
                e=dy-dx/2;
                while(x1>=x2)
                {
                    draw_point(x1,y1,color);
                    if(e>0){y1+=1;e-=dx;}   
                    x1-=1;
                    e+=dy;
                }
            }
            else        // 3/8 octant
            {
                e=dx-dy/2;
                while(y1<=y2)
                {
                    draw_point(x1,y1,color);
                    if(e>0){x1-=1;e-=dy;}   
                    y1+=1;
                    e+=dx;
                }
            }
        }
        else           // dy<0
        {
            dy=-dy;   // dy=abs(dy)
            if(dx>=dy) // 5/8 octant
            {
                e=dy-dx/2;
                while(x1>=x2)
                {
                    draw_point(x1,y1,color);
                    if(e>0){y1-=1;e-=dx;}   
                    x1-=1;
                    e+=dy;
                }
            }
            else        // 6/8 octant
            {
                e=dx-dy/2;
                while(y1>=y2)
                {
                    draw_point(x1,y1,color);
                    if(e>0){x1-=1;e-=dy;}   
                    y1-=1;
                    e+=dx;
                }
            }
        }   
    }
}

// 區域填色函式
// 引數:開始列數,結束列數,開始行數,結束行數,顏色
void fill_fb(int start_x, int end_x, int start_y, int end_y, unsigned short val)
{
    int i, j;
    for(i = start_y; i < end_y; i++)
    {
        for(j = start_x; j < end_x; j++)
        {
            fb[i*480 + j] = val;
        }
    }
}
void clr_fb(void)
{
    int i, j;
    for(i = 0; i < 272; i++)
    {
        for(j = 0; j < 480; j++)
        {
            fb[i*480 + j] = 0;
        }
    }
}


下面先簡要介紹常用的畫圓演算法(Bresenham演算法),然後再具體闡述筆者對該演算法的改進。

    一個圓,如果畫出了圓上的某一點,那麼可以利用對稱性計算餘下的七段圓弧:Plot(x,y),Plot(y,x),Plot(y,-x),Plot(x,-y),Plot(-x,-y),Plot(-y,-x),Plot(-y,x),Plot(-x,y)。

    1、Bresenham 畫圓演算法。Bresenham演算法的主要思想是:以座標原點(0,0)為圓心的圓可以通過0度到45°的弧計算得到,即x從0增加到半徑,然後利用對稱性計算餘下的七段圓弧。當x從0增加到時,y從R遞減到。

    設圓的半徑為R,則圓的方程為:

    f(x,y)=(x+1)2+y2-R2=0                                   (1)

    假設當前列(x=xi列)中最接近圓弧的畫素已經取為P(xi,yi),根據第二卦限1/8圓的走向,下一列(x=xi+1列)中最接近圓弧的畫素只能在P的正右方點H(xi+1,yi)或右下方點L(xi+1,yi-1)中選擇,如圖1所示。Bresenham畫圓演算法採用點T(x,y)到圓心的距離平方與半徑平方之差D(T)作為選擇標準,即

    D(T)=(x+1)2+y2-R2                                         (2)

    通過比較H、L兩點各自對實圓弧上點的距離大小,即根據誤差大小來選取,具有最小誤差的點為繪製點。根據公式(2)得:

    對H(xi+1,yi)點有:D(H)=(xi+1)2+yi2-R2;

    對L(xi+1,yi-1)點有:D(L)=(xi+1)2+(yi-1)2-R2;

    根據Bresenham畫圓演算法,則選擇的標準是:

    如果|D(H)|<|D(L)|,那麼下一點選取H(xi+1,yi);

    如果|D(H)|>|D(L)|,那麼下一點選取L(xi+1,yi-1);

    如果|D(H)|=|D(L)|,那麼下一點可以取L(xi+1,yi-1),也可以選取H(xi+1,yi),我們約定選取H(xi+1,yi)。

 

    圖1  Bresenham畫圓演算法點的選取

    綜合上述情況,得:

    當|D(H)|>|D(L)|時,選取L點(xi+1,yi-1)為繪製點座標;

    當|D(H)|<|D(L)|時,選取H點(xi+1,yi)為繪製點座標。

    然後將選取的點座標作為當前座標,重複上述過程直至xi=或者yi=為止,(xi,yi)的初始值為(0,R)。

    以上便是Bresenham演算法的主要思想,但是上述演算法是在一個假設下:以座標原點(0,0)為圓心。該假設實際上只是為了方便演算法的研究。但在實際嵌入式LCD顯示裝置中,往往圓心座標不是(0,0)點,而是以左上角為(0,0)點,這樣就使得在實際運用中,需要對這個演算法做很大的改進。

    另外,如果完全按照Bresenham畫圓演算法,那麼就會涉及到浮點運算,這使得嵌入式程式設計十分煩瑣,因為本系統中所有資料都是整型的,因此在這方面也要作一定的改進。下面根據本系統中嵌入式硬體特點和資料結構得特點,對這個演算法進行改進。

    2、改進的Bresenham畫圓演算法。先假設起始點為(R,0),令Pi=(xi,yi)為當前的一點,那麼我們就需要在Ti=(xi,yi+1)和Si=(xi-1,yi+1)中選取一點,如圖2所示。

 

    圖2 嵌入式LCD畫圓時點的選取

    設(xi-1/2+e,yi+1)為S和T之間圓上的點,e是S、T中點到圓上點的誤差,帶入圓的方程(1)得:

    f(xi-1/2+e,yi+1)=(xi-1/2+e)2+(yi+1)2-R2=f(xi-1/2,yi+1)+2(xi-1/2)e+e2=0   (3)

    在式(3)中,令

    di="f"(xi-1/2,yi+1)=-2(xi-1/2)e-e2                                    (4)

    如果e<0,那麼di>0,因此選擇S=(xi-1,yi+1),根據(3)與(4)得:

    di+1=f(xi-1-1/2,yi+1+1)=di-2(xi-1)+2(yi+1)+1=di+2(yi+1-xi+1)+1         (5)

    如果e30,那麼di£0,因此選擇T=(xi,yi+1),根據(3)與(4)得:

    di+1=f(xi-1/2,yi+1+1)=di+2yi+1+1                                  (6)

    起始點是(R,0)的時候,根據(4)得di的初始值d0就是:

    d0=f(R-1/2,0+1)=(R-1/2)2+1-R2=5/4-R=1-R(由於程式設計中所用資料型別均為整型,故取1-R)。

    綜合上述情況,得:

    當選取S=(xi-1,yi+1)時,那麼di+1=di+2(yi+1-xi+1)+1;

    當選取T=(xi,yi+1)時,那麼di+1=di+2yi+1+1;

    然後將選取的點座標作為當前座標,重複上述過程直至x=y,而不是xi=或者yi=,這樣就可以不用作浮點數計算了。

    本專案中的LCD畫素為640×480點陣,並且資料是八位的,當橫座標和縱座標超過255時,那麼資料就不能一次傳送成功,因此需要通過位元組操作來設定高位元組,然後再傳送低位元組。因此,每次畫圓上的點時要傳送的引數至少是六個,圓心座標是四個(因為要考慮圓心座標可能大於255,因此要對其圓心座標設定高、低位元組),另外兩個是圓上的點相對於圓心的座標,但是最後要畫一個點,需要四個引數,即改進的畫圓演算法為六個引數輸入,四個引數輸出。

    用C語言在嵌入式LCD上實現改進的Bresenham畫圓演算法的部分程式碼如下:

    voidMidBresenhamcircle(int R) 

    {

    intx,y,d;

    x="0";y=R;d=1-R;       

    while(x<y)

    {circlePoint(x,y);  

     if(d<0)  d+=2*x+3;  

    

       else

            {d+=2*(x-y)+5;

              y--;

             }

              x++;

     delay(900000);

     } 


令附上一個比較有意義的帖子http://www.dzsc.com/dzbbs/20050531/2007651911609418.html



以上內容 均來源於海之遙的部落格

純屬自己學習之用,分享一下,僅供學習交流