在DOS或者其他平台上高速执行图像显示的一些算法

软件开发大郭
0 评论
/
29 阅读
/
4454 字
30 2022-09

1.利用位移计算乘法

int count_offest(int _width,int x,int y) //移位乘法计算显示偏移值
{
 int mode[17]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};
 int i,offest=0;
 for (i=16;i>=0;i--)
 {
  if (_width>=mode[i])
  {
   offest+=(y<<i);
   _width-=mode[i];
   if (_width<1) break;
  }
 }
 return(offest+x);
}

2.高速绘制点(不用乘法)

void point(int x,int y,unsigned char color) //高速画点
{
 if ((color!=NO_COLOR)&&
  (x>=0)&&(x<SCR_H)&&
  (y>=0)&&(y<SCR_V))
 {
  #ifdef VESA_320_200
  *(buffer+(y<<8)+(y<<6)+x)=color;
  #endif
  #ifdef VESA_640_480
  *(buffer+(y<<9)+(y<<7)+x)=color;
  #endif
  #ifdef VESA_800_600
  *(buffer+(y<<9)+(y<<8)+(y<<5)+x)=color;
  #endif
  #ifdef VESA_1024_768
  *(buffer+(y<<10)+x)=color;
  #endif
  #ifdef VESA_1280_1024
  *(buffer+(y<<10)+(y<<8)+x)=color;
  #endif
 }
}

3.镂空算法

镂空算法很多,用的都是AND MASK+OR方式,不要去理它,每一个点要处理两遍,包含三次

读内存,两次逻辑运算,一次写内存,太慢了,在你的颜色中规定一种透明色,画点时不去管

它就行了(上例)。核心思想:每个点上少处理一次。

4.双缓冲

双缓冲是必要的,但也不全是,很多教课书上把双缓冲作为消除屏幕闪烁的唯一方法,这不

对,因为只要跟踪了屏幕刷新周期,就不会闪,双缓冲直接带来的就是你的程序画点必须画两次

,一次向buffer,另一次重buffer搬到屏幕。我在做优化时,往往首先把双缓冲优化掉。没必要

浪费时间,就算有点闪,游戏是可以牺牲效果,换取时间的。这里给一个跟踪屏幕刷新周期的函

数,只要在你的刷屏程序前加上,效果基本上就可以了。

void wait (void) //VGA屏幕刷新周期的测试
{
 while (inp(0x3DA)&0x08);
 while (!(inp(0x3DA)&0x08));
}

5.减少循环

减少循环,循环中多开销了一次累加(读写内存),一次比较(读内存+1次逻辑),如下例:

for (i=0;i<10000;i++)
 {
  *(p+i)=0;
 }

for (i=0;i<10000;i+=10)  //循环次数减少9000次
 {
  *(p+i+0)=0;
  *(p+i+1)=0;
  *(p+i+2)=0;
  *(p+i+3)=0;
  *(p+i+4)=0;
  *(p+i+5)=0;
  *(p+i+6)=0;
  *(p+i+7)=0;
  *(p+i+8)=0;
  *(p+i+9)=0;
 }

for (i=0;i<10000;i+=10)  //20次读变量内存减少为12次读,1次写
 {
  j=p+i;
  *(j+0)=0;
  *(j+1)=0;
  *(j+2)=0;
  *(j+3)=0;
  *(j+4)=0;
  *(j+5)=0;
  *(j+6)=0;
  *(j+7)=0;
  *(j+8)=0;
  *(j+9)=0;
 }

for (i=0;i<10000;i+=10)  //10次读值内存减少为1次,其余为寄存器变量
 {
  j=p+i;
  *(j+0)=*(j+1)=*(j+2)=*(j+3)=*(j+4)=*(j+5)=*(j+6)=*(j+7)=*(j+8)=*(j+9)=0;
 }

当然,如果允许,可以写10000个,不过也没必要,减掉一多半就行了。

关键在速度和程序容量上达成

平衡。

另外,DO...WHILE比FOR和WHILE要少一次逻辑比较。

6.图块处理

具体的说,处理一个图块时,很多人采用x,y两重循环,这是很值得研究的,根据屏幕特点,应该

只保留y循环,x方向直接线性累加处理即可。

7.不要解决判断语句

不要节约判断语句,它可能给你带来多一条语句的开销,但是却可能减少几百条语句的开销

8.良好的判断语句程序书写习惯

养成良好的书写习惯,让编译程序为你检查错误,如下例:

if (i==1)

写成 if (i=1) 编译不出错,但意思错了

写成 if (1=i) 编译就出错,可以检查出来

9.要程序流畅,主循环只做屏幕刷新的事情

主循环往往只是包含刷屏的一个死循环,更多的东东放在时钟里头,

要熟练拦截时钟,改变它的频率,你的画面就会动得流畅、自然。

下面是一个拦截时钟的例子,因为采用时钟循环,所以必须大量使用switch/case结构,要有思想准备。

#define TIME_KEEPER_INT 0x1c
long timer_counter;
void (_interrupt far *Old_Time_Isr)();
void timer_program(void);
////////////////////////////////////////////////////////////////
//注意:中断函数中不能调用系统输入输出函数,应尽量使用自己的程序
void _interrupt Timer(void)
{
 timer_program(); //调用用户程序
 timer_counter++;
 Old_Time_Isr();
}
////////////////////////////////////////////////////////////////
#define CTRL_8253 0x43
#define CTRL_WORD 0x3c
#define COUNTER_0 0x40
#define COUNTER_1 0x41
#define COUNTER_2 0x42
#define LOW_BYTE(n) (n&0x00ff)
#define HI_BYTE(n) ((n>>8)&0x00ff)
#define TIME_18HZ 0xFFFF
//改变定时器频率函数
//注意:超过1000Hz,与Windows将发生冲突
void Change_Timer(unsigned short new_count)
{
 outp(CTRL_8253,CTRL_WORD);
 outp(COUNTER_0,LOW_BYTE(new_count));
 outp(COUNTER_0,HI_BYTE(new_count));
}
////////////////////////////////////////////////////////////////
//安装时钟
void install_timer(int Hz)
{
 short time_hz;
 time_hz=short(1193180/Hz);
 timer_counter=0;
 Change_Timer(time_hz);
 Old_Time_Isr=_dos_getvect(TIME_KEEPER_INT);
 _dos_setvect(TIME_KEEPER_INT,Timer);
}
////////////////////////////////////////////////////////////////
//卸载时钟
void uninstall_timer()
{
 Change_Timer(TIME_18HZ);
 _dos_setvect(TIME_KEEPER_INT,Old_Time_Isr);
}
    暂无数据