SVGA 圖形編程教程

作為華中科技大學(xué)自動(dòng)化學(xué)院的學(xué)生,相信許多人在大二的時(shí)候會(huì)對(duì)C課設(shè)留下深刻的印象,而在 C課設(shè)中,SVGA 是大多數(shù)學(xué)生的第一道門(mén)檻,本文僅從 C 課設(shè)的需求角度對(duì) SVGA 作簡(jiǎn)要講解。

更新時(shí)間:2017-10-8。

預(yù)備知識(shí)

王士元的那本什么書(shū)是肯定要刷完的,刷完差不多就可以看這篇文章了。

SVGA初始化

類(lèi)似 VGA 的initgraph(),不過(guò)初始化 SVGA 需要手動(dòng)初始化,大家一般看到的代碼如下

unsigned char SetSVGAMode(unsigned int vmode)
{
    union REGS in,out;
    in.x.ax=0x4f02;                 //進(jìn)入設(shè)置SVGA模式
    in.x.bx=vmode;                  //SVGA 模式參數(shù)
    int86(0x10,&in,&out);           //輸入到中斷函數(shù)中
    if(out.h.ah==0x01)              //功能調(diào)用失敗
    {
        ReturnMode();               //退出當(dāng)前模式
        printf("Set SVGA error!"); //中斷
        getch();
        exit(1);
    }
    return(out.h.ah);
}

vmode 模式參數(shù)可取以下模式號(hào),不同模式號(hào)調(diào)用的窗口大小不同,這里只顯示常用的幾個(gè)

15位模式號(hào) 分辨率 顏色數(shù)
111H 640*480 64K
114H 800*600 64K

建議窗口不要太大,不然刷屏?xí)r會(huì)有明顯的延遲。 ReturnMode()為調(diào)用失敗后返回原模式的函數(shù),它需要自己手寫(xiě)

int ReturnMode()     
{
    union REGS in;

    in.h.ah=0;
    in.h.al=(char)0x03;     //返回文本模式
    int86(0x10,&in,&in);  /*中斷*/
    return 0;
}

畫(huà)點(diǎn)函數(shù)

作為 SVGA 下畫(huà)圖的最基本功能,必須要搞懂每一行代碼的意思

void Putpixel(int x, int y, long colorTable)
{
    long     pos;
    short far *VideoBuffer=(short far *)0xA0000000L;
    register int   NewPage = 0;
    long color=CalColor(colorTable);

    /*計(jì)算該點(diǎn)的在VRAM的位置*/
    /*這里假設(shè)設(shè)置的VRAM邏輯寬度為1600*/
    /*什么是邏輯寬度后面會(huì)講到*/
    pos = y *1600l+ x;      
    NewPage = pos >> 15; //計(jì)算顯示頁(yè)面號(hào)
    SelectPage(NewPage);   //選擇頁(yè)
    VideoBuffer[pos%65536] = color;
} 

大多數(shù) SVGA 卡采用類(lèi)似于擴(kuò)頁(yè)內(nèi)存 LIM/EMS 規(guī)范所使用的方法,把 VRAM 分成小塊(稱(chēng)為頁(yè)),分別映射到電腦提供的地址空間(稱(chēng)為窗口)上,并用單個(gè)可讀寫(xiě)窗口顯示圖像。此窗口可同時(shí)讀寫(xiě),一般情況下窗口的位置在以 0xA0000000L 為起點(diǎn)的地址上,尺寸為 64KB。超過(guò)64k 的 VRAM 的地址空間用頁(yè)映射機(jī)制分塊映射到電腦提供的地址上。 SVGA 的顏色值比較奇葩,在256色情況下跟顏色的索引號(hào)不同,我們需要計(jì)算 在 64K 高彩色模式下,顏色值共占一個(gè)字。B占0~4位,G占5~10位,R占11~15位。將各顏色分量移位組合后便可得到其顏色值:

long CalColor(long colorTable)
{
    unsigned long r,g,b;
    r=colorTable>>16;
    g=(colorTable-(r<<16))>>8;
    b=colorTable-(r<<16)-(g<<8);     
    r>>=3;
    g>>=2;
    b>>=3;
    return (r<<11)+(g<<5)+b;
}

確定了要顯示的點(diǎn)的位置后需要將頁(yè)面切換的相應(yīng)位置

unsigned int SelectPage(unsigned char index)   
{
    union REGS in,out;

    in.x.ax=0x4f05;
    in.x.bx=0;
    in.x.dx=index;
    int86(0x10,&in,&out); //中斷
    return 0;
}

05H 為調(diào)用頁(yè)面控制功能 index 為當(dāng)前窗口的頁(yè)面號(hào) VRAM 只能裝65536那么大,所以要求下余數(shù),然后再在該位置賦值為相應(yīng)顏色值

設(shè)置邏輯掃描線長(zhǎng)度

int SetScreenWidth(unsigned long pixels)   
{
    static union REGS r;

    r.h.bl=0;
    r.x.ax=0x4f06;
    r.x.cx=pixels;
    int86(0x10,&r,&r);/*中斷*/
    return 0;
}

pixels在本文中設(shè)置為1600 邏輯掃描線就是你顯示圖像時(shí)邏輯上一行顯示像素的多少,這跟實(shí)際屏幕上一行顯示像素多少不同,它可以更大,但不能無(wú)限大。這個(gè)一般跟你屏幕寬度保持相同即可。在涉及到畫(huà)面整體移動(dòng)時(shí)這個(gè)需要設(shè)置的更大,以便容納更多的圖像。作用如下圖所示

灰色部分為邏輯上的顯示屏,紅色部分為實(shí)際上的顯示屏。比如在地圖過(guò)大的情況下,用較大的邏輯屏存儲(chǔ)圖像,然后移動(dòng)實(shí)際上的顯示屏就可以實(shí)現(xiàn)生活中查看地圖的效果。

畫(huà)直線

/*************************
 * 函數(shù)名稱(chēng):Line
 * 函數(shù)功能:畫(huà)直線
 * 參數(shù):
 *      long x1, long y1:直線左端點(diǎn)
 *      long x2, long y2:直線右端點(diǎn)
 *      int width:直線寬度
 *      long colorTable:256色顏色表
 * 返回值:
 *      0:成功
 ************************/

int Line( double x1, double y1, double x2, double y2, int width, long colorTable )
{
    int     pointX;
    int     pointY;
    int  k,i,NewPage,OldPage;
     short far *VideoBuffer=(short far *)0xA0000000L;
    double  r,tcos,tsin,DX,DY;
    long pos;
    long color=CalColor(colorTable);

    for(i=0;i<width;i++)     
    {
        NewPage = OldPage=0; 
        pos=(y2*1600l+x2);      
        NewPage=OldPage=pos>>15;
        SelectPage(NewPage);
        DX = x1 - x2;
        DY = y1 - y2;
        r = sqrt(DX * DX + DY * DY);
        tcos = DX / r;
        tsin = DY / r;
        for ( k = 0; k <= r; k ++)         
        {            
            pointX = (int)(x2 + tcos * k);       
            pointY = (int)(y2 + tsin * k);       
            pos =(long)( pointY * 1600l+ pointX);     
            NewPage =pos >>15;
            if (NewPage != OldPage)
            {
                SelectPage(NewPage);
                OldPage= NewPage;
            }
            VideoBuffer[pos] = color;//設(shè)置顏色,可根據(jù)需要更改
        }
        if(tsin==0)
        {
            y1++;
            y2++;
        }
        else
        {
            x1++;
            x2++;
        }
    }
    return 0;
}

由于 SelectPage() 函數(shù)十分耗時(shí)間,因此我們要盡可能少用它,不能畫(huà)一個(gè)點(diǎn)就調(diào)用它一下。我們建立兩個(gè)變量 NewPage 和 OldPage 前者保存當(dāng)前要畫(huà)的點(diǎn)所在的 Page,后者保存之前的點(diǎn)所在的 Page,如果兩者一樣,則不需要切換頁(yè),直接畫(huà)點(diǎn)即可,直到兩者不一樣再切換畫(huà)面。 其他的就比較簡(jiǎn)單,主要是對(duì)直線上點(diǎn)的定位問(wèn)題,稍稍想一下就清楚了。

顯示圖片

/*************************
 * 函數(shù)名稱(chēng):ShowPic
 * 函數(shù)功能:讀取bmp文件到顯存
 * 參數(shù):
 *      int x, int y:圖片左上角坐標(biāo)
 *      char * FileName:文件地址
 * 返回值:
 *      0:成功
 *      -1:失敗
 ************************/
int ShowPic(int x,int y,char *FileName)
{
    int i,j,k=0;
    FILE *fp;
    char OldPage=0,NewPage=0;
    unsigned int DataOffset;
    long Width,Height,BitCount;
    unsigned long pos;
    short *buffer;
    short far *VedioBuffer=(short far *)0xA0000000L;//顯存初始地址指針 
    BITMAPINFOHEADER BmpInfoHeader;
    BITMAPFILEHEADER BmpFileHeader;

    if((fp=fopen(FileName,"rb"))==NULL)
    {
        ReturnMode();
        printf("Cannot read the picture\n\t\t%s",FileName);
        getch();
        return -1;
    }

    /* 讀取文件頭和信息頭 */
    fread(&BmpFileHeader, sizeof(BmpFileHeader),1,fp);
    fread(&BmpInfoHeader, sizeof(BmpInfoHeader),1,fp);
    Width = BmpInfoHeader.Width;
    Height = BmpInfoHeader.Height;
    DataOffset = BmpFileHeader.OffBits;
    BitCount = BmpInfoHeader.BitCount / 8;
    /*RAM start*/
    buffer=(short *)malloc(Width*sizeof(short));//申請(qǐng)內(nèi)存 
    if(buffer==NULL)
    {
        ReturnMode();
        printf("SVAGA.c_Malloc error! in function ShowPic!");
        getch();
        return -1;
    }

    /*  圖像的寬度(以字節(jié)為單位)必須是4的倍數(shù),倘若不到4的倍數(shù)則必須要用0補(bǔ)足。k來(lái)滿(mǎn)足字節(jié)對(duì)齊 */
    k=(Width*BitCount%4)?(4-Width*BitCount%4):0;
    OldPage=((Height-1+y)*1600l+x)>>15;//一定要轉(zhuǎn)成long
    NewPage=OldPage;
    SelectPage(OldPage);
    fseek(fp,DataOffset,SEEK_SET);
    for(i=Height-1;i>=0;i--)
    {
        fread(buffer,Width*BitCount+k,1,fp);  //讀取一行像素點(diǎn)的信息
        for(j=0;j<Width;j++)     //把讀取的一行像素點(diǎn)顯示出來(lái)         
        {             
            pos=((i+y)*1600l+j+x);//位置偏移量              
            NewPage=pos>>15;//計(jì)算第幾頁(yè) 
            if(NewPage!=OldPage)
            {
                SelectPage(NewPage);
                OldPage=NewPage;
            }
            VedioBuffer[pos&0x00007fff]=buffer[j];//寫(xiě)內(nèi)存 
        }
    }

    /* 關(guān)閉文件 */
    fclose(fp);
    free(buffer);
    return 0;
}

按照往年慣例,顯示圖片一般用 256 色的 bmp 圖片,因?yàn)榭梢杂型鶎玫拇a參考,如果你認(rèn)為你很牛逼想在課設(shè)驗(yàn)收老師面前炫技的話,盡情用你喜歡的圖片格式。

關(guān)于bmp怎么存放圖像的,建議大家上網(wǎng)找找答案,這里不再贅述。這里提供我當(dāng)年閱讀的文章bmp文件(此網(wǎng)頁(yè)已失效,大家自己去找吧)
剩下的注釋已經(jīng)寫(xiě)的很詳細(xì)了,有不懂可以在下面留言。

其他有用函數(shù)

C課設(shè)已經(jīng)驗(yàn)收完了,這里給出一些可能有用的源代碼,如果你認(rèn)真看了上文,下面的代碼你應(yīng)該看得懂。

/*************************
 * 函數(shù)名稱(chēng):GetBackground
 * 函數(shù)功能:動(dòng)畫(huà)背景摳圖
 * 參數(shù):
 *      int left,right:左右邊界
 *      int top,bottom:上下邊界
 *      short * buffer:圖片存儲(chǔ)地址
 * 返回值:
 *      0:成功
 ************************/
int GetBackground(int left,int top,int right,int bottom,short *buffer)
{
    int i,j;
    unsigned long pos,Width,Height;
    char OldPage,NewPage;
    short far *VideoBuffer=(short far *)0xA0000000L;
    Width=right-left;
    Height=bottom-top;
    OldPage=(long)(top*1600l+left)/32768;
    NewPage=OldPage;
    SelectPage(NewPage);
    for(i=0;i<Height;i++)
    {
        for(j=0;j<Width;j++)         
        {             
            pos=(long)((i+top)*1600l+j+left);             
            NewPage=pos/32768;             
            if(OldPage!=NewPage)             
            {                 
                SelectPage(NewPage);                 
                OldPage=NewPage;             
            }             
            buffer[i*Width+j]=VideoBuffer[pos%32768];         
        }     
    }     
    return 0; 
} 
/*************************  
 * 函數(shù)名稱(chēng):PutBackground  
 * 函數(shù)功能:背景重新顯示 
 * 參數(shù):  
 *      int left, right:左右邊界  
 *      int top, bottom:上下邊界  
 *      short * buffer:圖片存儲(chǔ)地址  
 *      long colorTable:不顯示的顏色  
 * 返回值:  
 *      0:成功  
 ************************/ 
int PutBackground(int left,int top,int right,int bottom,short *buffer, long colorTable) 
{     
    int i,j;    
    unsigned long pos,Width,Height;     
    char OldPage,NewPage;     
    short far *VideoBuffer=(short far *)0xA0000000L;  
    long color=colorTable>0 ? CalColor(colorTable) : colorTable;
    Width=right-left;
    Height=bottom-top;
    OldPage=(top*1600l+left)/32768;
    NewPage=OldPage;
    SelectPage(NewPage);
    for(i=0;i<Height;i++)
    {
        for(j=0;j<Width;j++)         
        {
            pos=((i+top)*1600l+j+left);   
            NewPage=pos/32768;        
            if(OldPage!=NewPage)        
            {
                SelectPage(NewPage);        
                OldPage=NewPage;        
            }          
            //if (color!=buffer[i*Width+j])    
            VideoBuffer[pos%32768]=buffer[i*Width+j];     
        }   
    }    
    return 0; 
}
/************************* 
 * 函數(shù)名稱(chēng):InputText 
 * 函數(shù)功能:檢測(cè)輸入字符并顯示在屏幕上 
 * 參數(shù):
 *      int x, y:起始位置 
 *      char * str:返回輸出字符串 
 *      0:成功  
 * 作者:lxr  
 ************************/ 
int InputText(int x,int y,char *str) 
{
    int i=0,j,ch; 
    int buffer[MAX][100];  
    while(1)   
    {
        ch=-1;   
        if(bioskey(1)!=0)    
        {           
            ch=bioskey(0); 
        }       
        else         
        {       
            GetBackground(x+i*8,y,x+i*8+8,y+16,buffer[i]);  
            VerticalLine(x+i*8,y,x+i*8,y+15,2,SVGA_WHITE);      
            delay(500);        
            PutBackground(x+i*8,y,x+i*8+8,y+16,buffer[i],-1);    
            delay(500);     
        }            
        if(ch==0x1c0d)   //回車(chē)鍵   
        {           
            break;    
        }      
        if(ch==0x0e08)      //退格鍵    
        {            
            if(i>0)
            {
                i--;
                str[i]='\0';
                PutBackground(x+i*8,y,x+i*8+8,y+16,buffer[i],-1);
            }
            else
            {
                i=0;
                str[i]='\0';
            }
            continue;
        }

        if(i<=10&&ch!=-1) //max=10
        {
            str[i]=(char)(ch&0xff);
            GetBackground(x+i*8,y,x+i*8+8,y+16,buffer[i]);
            printASC(x+i*8,y,str+i,SVGA_RED,1,1);
            i++;
            str[i]='\0';
        }
    }
    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,692評(píng)論 4 61
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程,因...
    小菜c閱讀 7,389評(píng)論 0 17
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,351評(píng)論 25 708
  • 之前寫(xiě)過(guò)一篇《寫(xiě)代碼的四個(gè)境界》,那個(gè)時(shí)候,大部分時(shí)候我還是愉快地寫(xiě)著自己的代碼。Code review 也是每天...
    Danny_Boy閱讀 1,062評(píng)論 0 50
  • aaa瘋了
    薄倖郎君閱讀 308評(píng)論 0 0

友情鏈接更多精彩內(nèi)容