DOS真彩色模式下真彩色图像显示技术

2016-11-18

摘要 该文通过介绍Cirrus GD5434卡等几种高、真彩色卡及其显示模式,详述了DOS环境下与硬件无关的VESA高彩色仿真真彩色、真彩色图像全息显示的一般性方法,还提示了24位PCX真彩色图像解压缩快速显示编程的要点。最后给出C语言编程示例。

一、高、真彩色显示卡

近年来,顺应CAD、CG及多媒体技术进步的要求,许多彩色适配器板卡厂商先后推出性能价格比较高的一系列高彩色 (High Color)和真彩色(True Color)SuperVGA显示卡。如Trident Microsystems公司的TGUI94XX、TGUI96XX,Cirrus Logic的GD543X、GD544X,Tseng Laboratories的ET4000, ARK Logic的ARK1000,、ARK2000,S3 Incorporated的 S3 86CXXX等等。它们除了继续支持标准VGA模式外,都支持16色、256色、32K高彩色、64K高彩色及16.7M真彩色VESA BIOS 扩展模式,版本大多为VESA 1.2, 卡上具有32K、64K及16.7M DAC,有16位ISA、32位VESA、32位 PCI总线等多种接口,显示VRAM配置一般有1M、2M、4M,一般都带有硬件加速的32位或64位图形加速引擎(Graphics Engine),满足了当今386~586各种档次的PC机对更多色彩、更高分辨率的要求。

表1整理了市场上常见的GD5434(64位 GUI)、TGUI9440AGi(32位GUI)和ARK2000PV(64位GUI)三种PCI总线高、真彩色显示卡的OEM BIOS调用模式号、色彩数、分辨率、VESA模式对照及其相应的VRAM占用等数据。其他支持 VESA 1.2的显示卡只要查阅卡附手册得到OEM(原始设备制造商)自定义的高、真彩色模式号或其对应的VESA模式号。有些配4M VRAM的显示卡甚至可支持1280×1024 16M色(VESA 1.2 11BH模式)。

表1

二、高、真彩色编程与256色编程的异同

高、真彩色模式编程在写视频缓冲区端口索引号、页切换方式、置模式号等方面类同扩展256色编程, 例如,Trident的高、真彩色显示卡,写视频缓冲区端口索引号仍是(0x3C4, 0xE)和(0x3C5, page^2),视频窗口页粒度仍是64, 页切换方式仍是64K 页、128K页任选。不同点在于:高、真彩色模式已经不再使用256组DAC寄存器索引号及调色板概念,而使用像素字长的RGB 分量数据直接描述色彩及饱和度,写视频缓冲区映射到VRAM后由新的64K DAC或16M DAC将色彩数据转为模拟信号送多频彩色显示器,256 色编程中有关调色板的BIOS中断全部失去作用;其次,由于用多个字节表示一个像素,高、真彩色DAC 转换的时间成倍增加,显示速度过分依赖卡上图形引擎(Graphics Engine)的效率,再加上数据成倍占用RAM或VRAM,所以显示速度明显慢于256色图像显示。

另外,不同厂商自定义的访问端口寄存器索引号的方式均各不相同,直接根据硬件特性的编程必然缺乏通用性。若按照这些显示适配卡都支持的 VESA ( VideoElectronics Standards Association)标准扩展BIOS功能调用接口编程,从而实现软件接口层次上的兼容性,所编程序便可在众多的Super VGA卡上运行,有关 VESA编程的详细资料请查阅有关书刊。

表2是高、真彩色像素的分量结构示意,是理解高、真彩色图像编程的关键。

表2

三、24位PCX图像格式简介

24位PCX图像的文件头同16色、256色的一样,共128字节, 其中每个像素所用的彩色位数(bit-perpixel)值为8,彩色平面数(color-planes)值为3, 不再使用调色板。24位PCX图像数据的存储仍采用有限行程压缩法,但却是把单个的RGB行作为三个位平面数据分别进行压缩存放,第一个位平面由该行所有红色像素组成;第二个位平面由该行所有绿色像素组成;第三个位平面由该行所有蓝色像素组成,因为行程编码方法并不是总能减小24位复杂图像的大小,所以对 24位PCX 文件进行解码得到的结果图像比原来的小也属正常。本文所用24 位PCX 文件格式符合PCPaintbrush Version 5标准,是从Photo Styler 1.0的TIF图例转换的。

将上述解压缩的数据用于显示时,需按显示卡硬件高、真彩色DAC送色彩信号的顺序--红绿蓝红绿蓝......--重新组织,才能正确地显示24位真彩色图像。这一点是最不同于其他用三字节行程编码的24位RGB真彩色图像(如 24位TGA)。其它格式24位的真彩色图像文件只是图像头处理及解压数据的方式不同,显示的原理则完全相同。文后所附例程作适当改变,就可用于24位 TIF、24位BMP、24位TGA 等图像文件的显示。

下面就640X480分辨率介绍32K、64K高彩色、16M真彩色模式显示24位PCX图像。

四、32K、64K高彩色仿真16M真彩色编程

现成的64K色图像很少,彩色扫描仪扫出的多为256色或24位真彩色, 许多图像处理软件包的图例也是同样情形。这里只好用24位真彩色图像经下述图示过程的位移合并,做成16位的64K高彩色像素字。这种取24位RGB分量高位的方法仿真显示真彩色图像,明亮部分的色彩层次能较好还原,低暗部分的色彩层次有微小损失,仿真效果很好。读者也可根据需要作其他位的取舍(如舍两头留中间),以使色彩还原最小失真。

图示中的空格为零。32K 高彩色仿真与此类似,只需将绿色分量也右移3位,与红色、蓝色分量一道做成最高位为零,低15位有效的一个字,送视频缓冲区便可。

具体编程要点如下:

1.调用VESA BIOS 4f02H 号功能置高彩色图形模式,成功后调用VESA BIOS4f01H号功能返回每线字节数Line-bytes及窗口页粒度Wingran等重要参数;

2.读图像头后直接读图像数据,按上述方法转换为一16位字后送视频缓冲区始址A000:0000,每一像素一个字,每送一个字到视频缓冲区,地址偏移量加2;

3.640×480分辨率下每根扫描线需1280字节,为提高显示速度,由Line-bytes预先算出每根扫描线始址存于addr数组备查,由于满屏需600K字节,故编程上仍需考虑切换VRAM页的情况。例程根据各种卡不同模式的窗口页粒度Wingran值,调用VESA BIOS 4f05H号功能访问硬件分页寄存器实现64K页模式切换(此时仍有一根扫描线跨两页的情形);

4.显示完毕,调用VESA BIOS 4f02H 号功能置模式03H恢复原文本模式。

五、16M真彩色编程

真彩色编程的关键是要了解图像数据的存放顺序及解压方法,其次注意读图像数据后按顺序要求作转换,否则图像色彩失真。其余过程同高彩色模式。

具体编程要点如下:

1.调用VESA BIOS 4F02H号中断置真彩色图形模式;成功后调用VESA BIOS4F01H号功能,返回每线字节数Line-bytes及窗口页粒度Wingran等重要参数;

2.读图像文件头后直接读图像数据,解压缩后按红绿蓝顺序送视频缓冲区始址A000:0000,每一像素三字节,每送一像素到视频缓冲区,地址偏移量加3;

3.在VESA 112H模式(640×480 16.7M Color)下,不同显示适配卡的每线字节数是不一样的, 如ARK2000PV 为1920, GD5434及TGUI9440为2048,S3 86C868为2560, 但由于使用VESA编程, 4F01H号功能能准确返回每线字节Line-bytes 值, 并预先算出每条扫描线始址存于addr数组,可提高显示速度,由于满屏需900K以上字节,故编程上仍需考虑切换VRAM页的情况,换页机制同64K色情形;

4.有些装2M VRAM的适配卡, 可在640×480 16.7M色情况下使用32位快速格式,如S3 86C868的112H模式及GD5434的76H模式, 每线字节为2560, 此时解压缩后按红绿蓝及一零字节顺序送视频缓冲区始址 A000:0000,每一像素4字节,每送一像素到视频缓冲区,地址偏移量加4,类似特殊情况,例程照此稍作修改便可;

5.显示完毕,调用VESA BIOS 4F02H号功能置模式03H,恢复原文本模式。

六、示范程序

以上两种编程实现见所附例程,程序在有ISA/VESA/PCI三种总线插槽的 OctekHippo12型主板、AMD DX4/100 CPU、8M RAM、EMC1024×768、28隔行扫描彩色显示器、Cirrus GD5434(2M VRAM)显示卡、Borland C++ 3.1 Small 模式下编译通过。但执行程序并不依赖所编译的硬件环境, 曾经在装有 1M VRAM 的TGUI9440、TGUI9680、ARK2000PV、S3 86C868 等VESA局部总线、PCI局部总线、甚至ISA总线的TVGA8900D(也支持VESA 1.2标准, 1M以下的OEM模式号同TGUI9440)显示卡的486~586各档PC机的DOS环境下均获通过。

示例程序对Super VGA卡VESA BIOS高、真彩色扩展模式编程具有一般性。对本文未提到的其它高、真彩色显示卡,只要其支持VESA标准(Version>1.1),不加修改或稍作修改便可使用;对更高分辨率,只要显示适配卡配2M VRAM, 便可作115H、116H、117H模式的编程, 例程中只要修改highcolor()、truecolor()两函数中n、m的宽高界值和addr的上界。但更重要的是: 程序运行以前用显示适配卡所配调整DRAM像素时钟或调整彩色显示器扫描频率的实用程序, 将像素时钟和扫描频率调整到该卡现行分辨率所要求的值上, 例如, Cirrus GD5434卡(2M VRAM)要在117H模式下顺利仿真显示16.7M真彩色, 须在DOS下先执行 CLMODE.EXE t640=60 t800=60t1024=60 t1280=0, 程序便可顺利显示高彩色图像。

// 24位PCX高、真彩色图像显示例程

#include <stdio.h>

#include <stdlib.h>

#include <dos.h>

#include <bios.h>

#include <conio.h>

#include <io.h>

#include <mem.h>

#include <fcntl.h>

#include <alloc.h>

unsigned long dataoffset,Line-bytes;

unsigned long addr;

unsigned int Curpage,Wingran,Y-res,width,height;

FILE *fp;

typedef struct { // PCX图像文件头格式

char manufacturer;

char version;

char encoding;

char bits-perpixel;

int xmin, ymin;

int xmax, ymax;

int hres;

int vres;

char palette;

char reserved;

char color-planes;

int bytes-perline;

int palettetype;

char filler;

} PCXHEAD;

struct mode infoblock {

unsigned int modeattr;

unsigned char winaattr;

unsigned char winbattr;

unsigned int wingran;

unsigned int winsize;

unsigned int winaseg;

unsigned int winbseg;

unsigned char *pagefunc;

unsigned int bytesperscanline;

unsigned int xres,yres;

unsigned char charx,chary;

unsigned char numberofplanes;

unsigned char bitperpixel;

unsigned char numberofbanks;

unsigned char memorymodel;

unsigned char banksize;

unsigned char numberofimagepages;

unsigned char Reserved;

unsigned char x;

} modeinfo;

void SetVesaMode(unsigned int mode);

void VesaInfo(unsigned int mode);

void map(void);

void Selectpage(unsigned int page);

void highcolor(void);

void truecolor(void);

main() // 主函数

{ PCXHEAD header;

char *filename,c;

printf("Please enter the 640X480 24bit RGB mode PCX fil

ename: ");

gets(filename);

if((fp=fopen(filename, "rb"))==NULL)

{

SetVesaMode(0x03);

puts("File reading error");

exit(1);

}

fread( (char *) &header, 1, sizeof(PCXHEAD), fp);

width = header.bytes-perline;

height = header.ymax - header.ymin + 1;

printf("Image information: Width=%d, Height=%d",width,h

eight);

if ((header.bits-perpixel==8)&&(header.color-planes==3)

) {

printf("

Type : 24bits RGB true colors");

printf("1...Emulating display 16M true color image with

64K high color");

printf("2...Display of 16M true color image");

printf("Press select 1 or 2 : ");

if ((c=getch())=='1') {

highcolor();

SetVesaMode(0x03);

}

else if (c=='2') {

truecolor();

SetVesaMode(0x03);

}

else {

printf("This is not high-color & true-color image !")

;

exit(1);

}

}

fclose(fp);

return 0;

}

// 设置VESA BIOS扩展模式函数

void SetVesaMode(unsigned int mode)

{ union REGS r;

unsigned int setmode=1;

r.x.ax=0x4f02;

r.x.bx=mode;

int86(0x10,&r,&r);

if (r.x.ax!=0x4f)

setmode=0;

else VesaInfo(mode);

Curpage=0xffff;

return(setmode);

}

// 返回VESA编程信息函数

void VesaInfo(unsigned int mode)

{ union REGS r;

struct SREGS sr;

r.x.cx=mode;

r.x.ax=0x4f01;

sr.es =FP-SEG(&modeinfo);

r.x.di=FP-OFF(&modeinfo);

int86x(0x10,&r,&r,&sr);

Wingran =modeinfo.wingran;

Line-bytes=modeinfo.bytesperscanline;

Y-res

=modeinfo.yres;

}

// 计算扫描线始址函数

void map(void)

{ register int i,j;

for(i=0; i<Y-res; i++)

addr =(unsigned long)(i)*Line-bytes;

}

// 选择视频窗口对准页函数

void Selectpage(unsigned int page)

{ union REGS r;

if (page!=Curpage) {

r.x.bx=0;

r.x.dx=page*64L/Wingran;

r.x.ax=0x4f05;

int86(0x10,&r,&r);

Curpage=page;

}

}

// 16位高彩色仿真24位PCX真彩色图像显示函数

void highcolor(void)

{ register int i, j;

unsigned int red, green, blue;

unsigned int *word, *wordptr;

int n, m, k, cnt, total;

unsigned long segmet;

unsigned char *pic, *p0,*p1,*p2;

unsigned char page, picdata;

SetVesaMode(0x111);

map();

Selectpage(0);

n=min(480,height);

m=min(640,width);

word =(unsigned int *)malloc(2*m);

wordptr=word;

for (k=0; k<3; k++)

pic=(unsigned char *)malloc(m);

p0=pic; p1=pic; p2=pic;

fseek(fp,0x80L,SEEK-SET);

for(i=0; i<n; i++) {

pic=p0; pic=p1; pic=p2;

word=wordptr;

for (j=0;j<3;j++) {

total = 0;

while(total < m) {

cnt = 1;

picdata = fgetc(fp);

if(0xc0==(0xc0 & picdata)) {

cnt = 0x3f & picdata;

picdata = fgetc(fp);

for (k=0; k<cnt; k++)

*pic++ =picdata;

}

else

*pic++ =picdata;

total+=cnt;

}

}

pic=p0; pic=p1; pic=p2;

for (j=0; j<m; j++) {

red = *pic++ >>3;

green= *pic++ >>2;

blue = *pic++ >>3;

red=red<<11; green=green<<5;

*word++ =red|green|blue;

}

word=wordptr;

for (j=0;j<2*(m-1);j+=2) {

segmet=addr+j;

page=segmet>>16;

if (segmet <= 65535L) {

poke(0xa000, addr+j, *word++ );

}

else {

Selectpage(page);

poke(0xa000, addr+j, *word++ );

}

}

}

getch();

free(wordptr); free(pic);

free(pic) ; free(pic);

}

// 24位PCX真彩色图像全息显示函数

void truecolor(void)

{ register int i, j;

unsigned char *pic, *p0,*p1,*p2;

unsigned char page, picdata;

int n,m,k,cnt,total;

unsigned long segmet;

SetVesaMode(0x112);

map();

Selectpage(0);

n=min(480,height);

m=min(640,width);

for (k=0; k<3; k++)

pic=(unsigned char *)malloc(m);

p0=pic; p1=pic; p2=pic;

fseek(fp,0x80L,SEEK-SET);

for(i=0; i<n; i++) {

pic=p0; pic=p1; pic=p2;

for (j=0;j<3;j++) {

total = 0;

while(total < m) {

cnt = 1;

picdata = fgetc(fp);

if(0xc0==(0xc0 & picdata)) {

cnt = 0x3f & picdata;

picdata = fgetc(fp);

for (k=0; k<cnt; k++)

*pic++ =picdata;

}

else

*pic++ =picdata;

total+=cnt;

}

}

pic=p0; pic=p1; pic=p2;

for (j=0;j<3*(m-1);j+=3) {

segmet=addr+j;

page=segmet>>16;

if (segmet <= 65535L) {

for (k=0; k<3; k++)

pokeb(0xa000, addr+j+k, *pic++);

}

else {

Selectpage(page);

for (k=0; k<3; k++)

pokeb(0xa000, addr+j+k, *pic++);

}

}

}

getch();

for (k=0; k<3; k++) free(pic);

}

参考文献

1.张一波.Super VGA与VESA编程指南.北京:海洋出版社,1993.8

2.张益明.微机图像文件格式大全(续篇).北京:学苑出版社,1994.7

更多相关阅读

最新发布的文章