作為華中科技大學(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;
}