荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Peter (小飞侠), 信区: Program
标  题: 微机交互式绘图时VGA图像的存储与恢复
发信站: BBS 荔园晨风站 (Wed Jan 27 17:51:43 1999), 转信


采用微机交互式绘图不仅能避免编程时碰到的难以确定座标的麻烦,
而且使操作者直接参与绘图的过程,可以如同在纸上作图一样来布局
自己的图形位置。而交互式绘制的图像必须能保存在磁盘文件中,并
能将已保存在磁盘中的图像重新显示在屏幕上,否则,交互式绘图技术
便会黯然失色。
    本文介绍三种不但可有效地存取VGA整屏图像,而且能实现存取任
意大小图块的方法及其Turbo C函数。

    方法之一
    C库函数中有getimage()与putimage()对图块复制的操作函数,但
描述图块的总字节数大小不能超过64K字节,而存储16色、640×480标
准VGA整屏图像需达150K字节之多。一种方法是将整帧图分为三个图
块分别存储于磁盘文件中。即使如此,用getimage()库函数之前仍需
开辟50K以上的缓存区,所需占用的内存仍是较大的,尤其是在编制大
型交互式绘图软件时,更会显得内存紧张,甚至造成死机现象。为了节
省缓存可采用将整屏图切割成更小的图块的方法,如分成480行块,这
样仅需要324字节的缓存空间。但如此存储每块图形又需比描述图像
信息实际的320字节多4个字节,共需多占480×4即1920字节磁盘空间
。能否进一步省去这些多余的字节呢?答案是肯定的。
    经探索发现getimage()函数开辟的缓存数组的第1、2个元素是描
述图块的宽度x,第3、4个元素是描述图块的高度y,设图块的左上角与
右下角的坐标分别是(x1,y1)和(x2,y2),缓存数组为buf[],则:
    x=buf[0]+buf[1]*256;
    y=buf[2]+buf[1]*256;
    对于存储480行图块的数组,前4个元素均分别为127,2,0,0,因而
不必存储,再现图像时在数组前4个元素中一一赋予这4个相应数值则
可。根据上述原理用Turbo C编制整屏存图与取图的函数save-scrpic
-1()与loadscrpic-l()的源程序如下(头文件略):
    save-scrpic-l(char *pic-name) {FILE *fp;
      register unsigned int i,j;
      char buf[324];
      fp=fopen(pic-name,"wb");
      for(j=0;j<480;j++)
      {getimage(0,j,639,j,buf);
       for(i=4;i<324;i++)
       fputc(buf[i],fp);
       }
       fclose(fp);
      }
      loadscrpic-l(char *pic-name)
      {FILE  *fp;
       register unsigned int i,j;
       char buf[324]=(127,2,0,0);
       fp=fopen(pic-name,"rb");
       for(j=0;j<480;j++)
       (for(i=4;i<324;i++)
        buf[i]=fgetc(fp);
        putimage(0,j,buf,COPY-PUT);
       }
       fclose(fp);
      }
    源程序中的参数*pic-name为存储图像的磁盘文件名。
    类似地可以编制出存储与再现任意大小图块的C函数save-subpic
-1()与loadsubpic-1( ),不同的是存储图块的磁盘文件必须先用前4
个字节描述图块的宽度x=x2-x1和高度y=y2y1。由于图块的恢复可以
任意指定左上角,不必与原位置相同,故不必保存原图块的左上角坐标
( x1,y1)与右下角的坐标(x2,y2)。
    save-subpic-1(int x1,int y1,int x2,int y2,char *pic-name
)
    {FILE *fp;
     register int i,j;
     int x;
     unsigned int size;
     char buf[324];
     for(i=4;i<324;i++)
       buf[i]=0;
     x=x2-x1;
     fp=fopen(pic-name,"wb");
     fputc(x%256,fp);
     fputc(x/256,fp);
     fputc(y2y1)%256,fp);
     fputc(y2y1)/256,fp);
     for(j=y1;j<y2+1;j++)
     {getimage(x1,j,x2,j,buf);
      for(i=4;i<(4+(x+1.5)/2);i++)
        fputc(buf[i],fp);
     }
     fclose(fp);
    }
    loadsubpic-1(int x1,int y1,char *pic-name)
    {FILE *fp;
     register int i,j;
     int x,y;
     char buf[326];
     for(i=4;i<324;i++)
       buf[i]=0;
     fp=fopen(pic-name,"rb");
     for(i=0;i<2;i++)
     {buf[i]=fgetc(fp);
      buf[i+2]=0;
    }
    x=buf[0]+buf[1]*256;
    y=fgetc(fp)+fgetc(fp)*256;
    for(j=y1;j<=y1+y;j++)
     {for(i=4;i<(4+(x+1.5)/2);i++)
       buf[i]+fgetc(fp);
      putimage(x1,j,buf,COPY-PUT);
     }
     fclose(fp);
    }
    将上述源程序中的COPY-PUT换成XOR-PUT、OR-PUT、NOT-PUT则可
实现将磁盘中存储的图像与原屏幕图像异或、或、反相叠加。

    方法之二
    另一种方法则是利用C中读点与写点的函数来实现图形的存储与
再现,此法存取图像速度慢些,但不需占用内存,且兼容性好,可靠性高

    对于16色的图像,存储每个象素点需要4个位,即每个字节可描述
两个点的颜色值,第1个点在高4位,第2个点在低4位。故存图的磁盘文
件每字节的值c应为读取第1点的颜色值左移4 位后再加上同一行相邻
第2点的颜色值:
    c=(getpixel(i,j)<<4)+getpixel(i+1,j);
    而图像的恢复则相反,从磁盘文件中每读取1个字节后,右移4位则
为第1点dot1的颜色值,而同一行相邻第2个点的颜色值dot2必须将该
字节的高4位值屏蔽,即将该字节与二进制000 01111(十进制数为15)
相与:dot2&15。用此法编写的全屏幕图像存取的c函数如下:
    save-scrpic-2(char *pic-name)
    {FILE *fp;
     register int i,j;
     fp=fopen(pic-name,"wb");
     for(j=479;j>=0;j--) /*从底到顶*/
     for(i=0;i<639;i+=2) /*从左到右*/
     fputc((getpixel(i,j)<<4)+getpixel(i+1,j),fp);
     fclose(fp);
    }
    loadscrpic-2(char *pic-name)
    {FILE *fp;
     register int i,j;
     unsigned char dot1,dot2;
     fp=fopen(pic-name,"rb");
     for(j=479;j>=0;j--) /*从底到顶*/
     for(i=0;i<639;i+=2) /*从左到右*/
     {dot1=dot2=fgetc(fp);
      if(dot1>>=4 putpixel(i,j,dot1);
      if(dot2&=15) putpixel(i+1,j,dot2);
     }
     fclose(fp);
    }
    之所以图像阵列的存取采用从底到顶而不是从上到下的顺序是为
了与BMP图像文件格式相兼容之故。下面是加上存储16色BMP图像块文
件头的源程序。
    save-bmp(int x1,int x2,int y1,int y2,char *pic-name)
    {unsigned char bmp-head[118]=
     {66,77,118,88,2,0,0,0,0,0,
      118,0,0,0,40,0,0,0,128,2,
      0,0,224,1,0,0,1,0,4,0,
      0,0,0,0,0,0,0,0, 0,0,
      0,0,0,0,0,0,0,0, 0,0,
      0,0,0,0,0,0,0,0,128,0,
      0,0,0,128,0,0,128,128,0,0,
      0,0,128,0,128,0,128,0,0,128,
      128,0,192,192,192,0,128,128,128,0,
      255,0,0,0,0,255,0,0,255,255,
      0,0,0,0,255,0,255,0,255,0,
      0,255,255,0,255,255,255,0);
      FILE  *fp;
      int i,j,x,y,n,dot1,dot2;
      x=x2-x1+1;
      y=y2y1+1;
      bmp_head[18]=x%256;
      bmp_head[19]=x/256;
      bmp_head[22]=y%256;
      bmp_head[23]=y/256;
      if(x%8==0)  n=x;
      else  n=x+8-x%8;
      fp=fopen(pic_name,"wb");
      fwrite(bmp_head,118,1,fp);
      for(j=y2;j>=y1;j--)
       (for(i=x1;i<=x1+n;i+=2)
        (dot1=getpixel(i,j);

    dot2=getpixel(i+1,j);

    if(i>=x1+x)  dot1=dot2=0;

    fputc((dot1<<4)+dot2,fp);

    }
        }
        fclose(fp);
    }

    方法之三
    方法三则是直接对显示器缓存区操作,因而运行速度较快,但与硬
件有关。标准VGA整屏图像有640×480象素点,显示内存被组织成4个
可独立寻址的存储位平面,且这4个存储位平面所占的地址空间相同,
起始地址均为OXA0000H,屏幕上的一个象素点由蓝、绿、红、灰(亮度
)4层图像位平面中各1位组合而成,可组合成16种颜色,每个位平面的1
个字节代表连续8个点的颜色状态。整个屏幕需占地址相同的4个位平
面中各38400个字节,而每行需占4个位平面中地址相同的各80个字节
。因而屏幕上象素点(i,j)此时相对于显示内存起始地址的偏移量为j
*80+i/8。
    要访问显示内存需对寄存器正确操作。I/O端口值为3CEH的是图
形地址寄存器,3CFH是图形数据寄存器。赋予3CEH不同的值,可选择不
同的图形数据寄存器作为当前活动寄存器。若赋予3CEH值为04时,便
选择了读出彩色图像页面选择寄存器(端口值为3CFH),而置3CFH为0、
1 、2、3时便决定了代码为0、1、2、3的4层位图像页面中的对应位
平面来读。据此可编制出存储整屏图像及任意大小图块的函数save-s
crpic-3()及save-subpic-3()。
    在VGA屏幕上恢复显示图形,关键在于对定序器输出寄存器的正确
控制。定序器地址寄存器的端口值为3C4H;定序器数据寄存器的端口
值为3C5H。置3C4H端口值为不同的值可以选择不同的定序器数据寄存
器作为当前活动寄存器,当赋予端口3C4H的值为02时便选择了映射屏
蔽寄存器(端口值仍为3C5H)。
    对端口3C5H分别置1、2、4、8时,则选择了蓝、绿、红、灰(亮度
)四个颜色显示页面来写。据此可编写出将存储于磁盘中的图像写入
显示缓存区的函数loadscrpic-3()和loadsub pic-3()。
    save-scrpic-3(char *pic-name)
    {register unsigned i;
     char far *ptr=(char far *)MK-FP(0xa000,0x0000);
     FILE *fp;
     fp=fopen(pic-name,"wb");
     for(i=0;i<38400;i++)
     {outportb(0x3ce,4);
      outportb(0x3cf,0);
      fputc(*ptr,fp);
      outporrb(0x3cf,1);
      fputc(*ptr,fp);
      outportb(0x3cf,2);
      fputc(*ptr,fp);
      outportb(0x3cf,3);
      fputc(*prt,fp);
      ptr++;
     }
     fclose(fp);
     outportb(0x3ce,4);
     outportb(0x3cf,0);
    }
    loadscrpic-3(char *pic-name)
    {register unsigned i;
     char far *ptr=(char far *)MK-FP(0xa000,0x0000);
     FILE *fp;
     fp=fopen(pic-name,"rb");
     for(i=0;i<38400;i++)
     (outportb(0x3c4,2);
      outportb(0x3c5,1);
     *ptr=fgetc(fp);
      outportb(0x3c5,2);
      *ptr=fgetc(fp);
      outportb(0x3c5,4);
      *ptr=fgetc(fp);
      outportb(0x3c5,8);
      *ptr=fgetc(fp);
      ptr++;
     }
    fclose(fp);
     outportb(0x3c4,2);
     outportb(0x3c5,0xf);
    }
    同样,图块的存取须在磁盘文件中前4个字节记录图块的宽度和高
度。限于篇幅,略去该方法图块存取的源程序。
    讨论

    1.本文三种方法均可有效地存取VGA整屏图像,也可存取任意大小
的图块。文中提供的源程序可直接用作开发交互式绘图软件图像存取
的功能模块。
    2.方法三的运行速度最快的,方法一次之,方法二较慢;方法一和
方法二存于磁盘中的图像再现时可与原屏幕图像叠加;方法二和方法
三不耗缓存,方法一也仅需324字节的缓存;方法二存储的图像便于放
大、缩小、旋转等变化后恢复于屏幕。
    3.本文存取图块的方法不消耗缓存,不受64K字节的限制,可方便
用于交互式绘图时(利用虚拟盘或硬盘)保存与恢复弹出或下拉菜单前
的屏幕图像。
    4.方法二存取图像阵列的方式与未压缩的BMP图像阵列的格式是
相同的,因而用此法存储的图像文件加上相应BMP图像的文件头即成BM
P图像文件。这样就可实现用C绘制的科技图形用此法存盘而被WINDOW
S下的图形软件或图文编辑软件所调用。

--

--
※ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.1.3]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店