SVGA 圖形編程教程

作為華中科技大學(xué)自動化學(xué)院的學(xué)生魁巩,相信許多人在大二的時候會對C課設(shè)留下深刻的印象,而在 C課設(shè)中陆蟆,SVGA 是大多數(shù)學(xué)生的第一道門檻,本文僅從 C 課設(shè)的需求角度對 SVGA 作簡要講解允耿。

更新時間:2017-10-8。

預(yù)備知識

王士元的那本什么書是肯定要刷完的扒怖,刷完差不多就可以看這篇文章了较锡。

SVGA初始化

類似 VGA 的initgraph(),不過初始化 SVGA 需要手動初始化盗痒,大家一般看到的代碼如下

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ù)可取以下模式號蚂蕴,不同模式號調(diào)用的窗口大小不同,這里只顯示常用的幾個

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

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

int ReturnMode()     
{
    union REGS in;

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

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

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

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

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

大多數(shù) SVGA 卡采用類似于擴(kuò)頁內(nèi)存 LIM/EMS 規(guī)范所使用的方法鸟整,把 VRAM 分成小塊(稱為頁),分別映射到電腦提供的地址空間(稱為窗口)上朦蕴,并用單個可讀寫窗口顯示圖像篮条。此窗口可同時讀寫,一般情況下窗口的位置在以 0xA0000000L 為起點(diǎn)的地址上吩抓,尺寸為 64KB涉茧。超過64k 的 VRAM 的地址空間用頁映射機(jī)制分塊映射到電腦提供的地址上。 SVGA 的顏色值比較奇葩疹娶,在256色情況下跟顏色的索引號不同,我們需要計算 在 64K 高彩色模式下伴栓,顏色值共占一個字。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ī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)用頁面控制功能 index 為當(dāng)前窗口的頁面號 VRAM 只能裝65536那么大额港,所以要求下余數(shù)饺窿,然后再在該位置賦值為相應(yīng)顏色值

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

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è)置的更大忍宋,以便容納更多的圖像。作用如下圖所示

灰色部分為邏輯上的顯示屏风罩,紅色部分為實(shí)際上的顯示屏糠排。比如在地圖過大的情況下,用較大的邏輯屏存儲圖像超升,然后移動實(shí)際上的顯示屏就可以實(shí)現(xiàn)生活中查看地圖的效果入宦。

畫直線

/*************************
 * 函數(shù)名稱:Line
 * 函數(shù)功能:畫直線
 * 參數(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ù)十分耗時間,因此我們要盡可能少用它乾闰,不能畫一個點(diǎn)就調(diào)用它一下落追。我們建立兩個變量 NewPage 和 OldPage 前者保存當(dāng)前要畫的點(diǎn)所在的 Page,后者保存之前的點(diǎn)所在的 Page涯肩,如果兩者一樣轿钠,則不需要切換頁,直接畫點(diǎn)即可病苗,直到兩者不一樣再切換畫面疗垛。 其他的就比較簡單,主要是對直線上點(diǎn)的定位問題硫朦,稍稍想一下就清楚了贷腕。

顯示圖片

/*************************
 * 函數(shù)名稱: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));//申請內(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來滿足字節(jié)對齊 */
    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)顯示出來         
        {             
            pos=((i+y)*1600l+j+x);//位置偏移量              
            NewPage=pos>>15;//計算第幾頁 
            if(NewPage!=OldPage)
            {
                SelectPage(NewPage);
                OldPage=NewPage;
            }
            VedioBuffer[pos&0x00007fff]=buffer[j];//寫內(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)頁已失效蔑鹦,大家自己去找吧)
剩下的注釋已經(jīng)寫的很詳細(xì)了,有不懂可以在下面留言箕宙。

其他有用函數(shù)

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

/*************************
 * 函數(shù)名稱:GetBackground
 * 函數(shù)功能:動畫背景摳圖
 * 參數(shù):
 *      int left,right:左右邊界
 *      int top陷寝,bottom:上下邊界
 *      short * buffer:圖片存儲地址
 * 返回值:
 *      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ù)名稱:PutBackground  
 * 函數(shù)功能:背景重新顯示 
 * 參數(shù):  
 *      int left, right:左右邊界  
 *      int top, bottom:上下邊界  
 *      short * buffer:圖片存儲地址  
 *      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ù)名稱:InputText 
 * 函數(shù)功能:檢測輸入字符并顯示在屏幕上 
 * 參數(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)   //回車鍵   
        {           
            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)容合作請聯(lián)系作者
  • 序言:七十年代末锅很,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子凤跑,更是在濱河造成了極大的恐慌爆安,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仔引,死亡現(xiàn)場離奇詭異扔仓,居然都是意外死亡褐奥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門翘簇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撬码,“玉大人,你說我怎么就攤上這事版保∷H海” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵找筝,是天一觀的道長蹈垢。 經(jīng)常有香客問我,道長袖裕,這世上最難降的妖魔是什么曹抬? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮急鳄,結(jié)果婚禮上谤民,老公的妹妹穿的比我還像新娘。我一直安慰自己疾宏,他們只是感情好张足,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坎藐,像睡著了一般为牍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上岩馍,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天碉咆,我揣著相機(jī)與錄音,去河邊找鬼蛀恩。 笑死疫铜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的双谆。 我是一名探鬼主播壳咕,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼顽馋!你這毒婦竟也來了谓厘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤趣避,失蹤者是張志新(化名)和其女友劉穎庞呕,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡住练,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年地啰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讲逛。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡亏吝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盏混,到底是詐尸還是另有隱情蔚鸥,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布许赃,位于F島的核電站止喷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏混聊。R本人自食惡果不足惜弹谁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望句喜。 院中可真熱鬧预愤,春花似錦、人聲如沸咳胃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽展懈。三九已至销睁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間标沪,已是汗流浹背榄攀。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工嗜傅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留金句,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓吕嘀,卻偏偏與公主長得像违寞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子偶房,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容