现在的位置: 首页 > 综合 > 正文

如何利用TC进行图形编程

2013年07月18日 ⁄ 综合 ⁄ 共 3740字 ⁄ 字号 评论关闭

/*叶子闲居   http://lxleaves.126.com   转载请保留此处*/
时常在一些论坛上看见有些人贴出如何利用TC进行图形编程,或者有些人询问如何进行GUI编程。无意发现国内这方面的资料委实很少,所以就没掂量掂量自己就贸然写了这篇文字,还望诸多高手海涵。

对于基本的图形函数,TC为我们提供的图形库是在远远不能满足需要,所以为了能够完善的显示出精彩的图形效果,我们需要利用VESA标准,具体中断调用为
int  
10h(AX=4f??h)。考虑到256色比较容易处理(其实其它也差不多,只是256色的图形文件比较节省空间,而且显示出来的效果足以满足需要,所
以就…)显示模式为1024*768*256,以下是C语言显示图形方式的子函数:
void   init256(char   flag){
if(flag==1){ /*如果传入的参数为1,则置1024*768*256模式*/
_AX=0x4f02;
_BX=0x105;
geninterrupt(0x10);
}else{ /*如果参数为0,则返回文本模式*/
_AX=3;
geninterrupt(0x10);
}
}

TC提供比较方便的中断调用方法,_AX表示AX寄存器,如果其中的数据为4f02h,表示置VESA模式,_BX保存的是模式
号,geninterrupt(0x10)表示调用10h中断,这是个显示中断,VESA利用它的4f??h为作为SuperVGA的中断入口。(需要包
含头文件dos.h)

现在给大家灌输分页的概念,如果你已经对分页已经耳熟能详就跳过这段。我们要知道在pc系统中对显存是没法进行直接操作的,必须映射到内存的一段,但是因
为内存的限制,只能为显存提供一个段用于其映射的空间,也就是每次只能访问64K的显存,所以如果显示分辨率比较低,而且色彩比较小,这64K的空间勉强
覆盖整个屏幕所需要的显存,但是对于1024*768*256的显示方式,就需要1024*768=786432字节的空间,也就是需要12*64K的空
间才够,但是内存有限,所以必须采用换页机制,也就是当需要改变显存内容时将需要改变的地方映射到内存上,再进行操作,以至于可以访问整个显存。

因此,如何分页以及在何处分页是个重要的环节。因为调用中断进行分页会有相对较大的时间延迟,如果频繁地调用分页中断势必影响屏幕的显示速度,一个合理的
算法与每次调用中断会有天壤之别。比较简单的算法是首先用一个寄存量存取一个当前页的索引号,然后在每个画点处分析是否需要换页,即比较显存位置是否已经
映射到内存上,如果已经映射,则直接写显存(内存映射),如果没有映射,则调用换页中断,如此可以大大提高画图速度。由于具体的换页算法不是本文讨论的重
点,而且这种算法已经可以满足需要,所以本文写点采用这种最简单的算法。
void   selectpage(){
int   newpage=ramoffset/65536;

if(newpage!=nowpage){
_AX=0x4f05;
_BX=0;
_DX=newpage;
geninterrupt(0x10);

nowpage=newpage;
}
}

其中ramoffset(显存偏移)和nowpage(当前页)为全局变量,因为selectpage为子函数,在调用之后一切变量都不再存
在,newpage(待换页)则不需要这样。该换页函数首先计算当前显存偏移所在的页newpage(65536是64K,一页的大小),然后比较是否与
当前页相同(nowpage),如果相同则返回,否则执行换页,并刷新当前页中储存的页号码。

画点采用直接写显存的方法,这种方法的优点是速度很快。
void   putpixel(int   x,int   y){
if(x> 1023||x <0||y> 767||y <0)return;
if((COLOR&0xff00)==0x100)return;
ramoffset=(long)y*1024+x;
selectpage();
if(WRITE_MODE==0)*(vram+ramoffset)=COLOR&0x00ff;
else   *(vram+ramoffset)^=COLOR&0x00ff;
}

首先比较x,y是否越界,如果越界则返回,不执行任何操作;如果COLOR(全局变量)的高8位等于
1((COLOR&0xff00)==0x100),表示该点为透明,同样返回;否则计算显存偏移地址,1024*768*256的偏移地址计算
方法为((long)y*1024+x)*1,因为256色为伪彩,只占有1字节,通过256个索引来访问调色板,接着调用换页函数,再检查写点模式
WRITE_MODE,如果为0,表示直接覆盖,则写点方法为*(vram+ramoffset)=COLOR&0x00ff,这里需要剔出高位
(其实不剔出也没有关系),如果WRITE_MODE为非0,则与原色彩进行异或操作(连续偶数次写点不会改变原来的色彩)。
下面跳过其它基于写点的绘图操作,因为一切都是算法问题,只要注意优先完成一个页的绘制,再进行换页,尽量少换页(指的是绘点时同一个页中优先绘制)。

TC中环境读取最基本的是bioskey函数,它可以用来判断是否有键盘中断的产生。bioskey(0)表示读取键盘缓冲区,并取出按键
值;bioskey(1)表示读取键盘缓冲区,但不取出按键值,可以用来判断缓冲区是否有值,功能等同于kbhit()函数;bioskey(2)表示读
取扩展按键的值,可以用来结合常规按键判断扩大按键功能,具体对应的值可以参考相关资料。
因为TC2.0本身没有支持鼠标的函数,所以必须自己调用系统中断来解决
int   mouse_reset(void){
_AX=0;
geninterrupt(0x33);
if(_AX==0)   return   0;
return   1;
}
如果成功返回0,否则返回1,另外还需要对鼠标范围进行设定
void   mouse_area(void){
_AX=7; /*     X     */
_CX=0; /*min   X*/
_DX=1023; /*max   X*/
geninterrupt(0x33);
_AX=8; /*     Y     */
_CX=0; /*min   Y*/
_DX=768; /*max   Y*/
geninterrupt(0x33);
}
因为是1024*768所以X最大为1023,最小为0,Y最大为767,最小为0,下面是对鼠标状态的读取
void   mouse_read(void){
_AX=3;
geninterrupt(0x33);
mousex=_CX;
mousey=_DX;
mousekey=_BX;
}
mousex(鼠标X值),mousey(鼠标Y值),mousekey(鼠标按键)均为全局函数
相关的鼠标函数为:

/*用于读取鼠标按键是否被按下,如果按下,则返回1,否则返回0*/
int   mouse_hit(void){
mouse_read();
if(mousekey!=0)return   1;
return   0;
}
/*用于检测鼠标是否移动,方法是比较当前鼠标坐标是否与原来坐标等同*/
int   mouse_move(void){
mouse_read();
if(mousex!=mousemovex){
mousemovex=mousex;
return   1;
}
if(mousey!=mousemovey){
mousemovey=mousey;
return   1;
}
return   0;
}
以下具体讲解GUI模式消息处理过程,首先完成窗口的初始化操作,包括绘制必要的元素,这些元素都是用结构体来储存数据,包括坐标,文字内容等,方便对其的访问。然后开始监听消息
while(1){
int   message=listen(1);
switch(message){
case   NONE:autorefresh();break;
case   MOUSEHIT:yourmousefun();break;
case   KEYBOARD:yourkbfun();break;
}
}
listen函数实现方法为:
int   listen(unsigned   char   flag){
char   ALL=1;
mousekey=0;
while(1){

/*绘制鼠标图像*/
mouse_pushbmp();

while(1){
if(mouse_hit()){
/*如果是鼠标击键,拿掉鼠标图像,并退出循环(向系统发送消息)*/
mouse_popbmp();
return   MOUSEHIT;
}
/*如果没有键盘或鼠标动作则持续循环*/
if(!mouse_move()&&!kbhit())continue;

/*如果是键盘操作,拿掉鼠标图像,并退出循环(向系统发送消息)*/
if((kb.key=bioskey(1))!=0){
mouse_popbmp();
return   KEYBOARD;
}

/*至此确定是鼠标移动操作,拿掉鼠标图像,跳出一级循环*/
mouse_popbmp();
/*如果传入的参数为ALL,表示监听一切,则在此返回,否则继续循环*/
if(flag==ALL)return   MOUSEMOVE;
break;
}
}
}
/*待续,写好一点,先贴上来*/

抱歉!评论已关闭.