如何在WIN-TC中或TC++3.0中把一张BMP格式的图片显示出来?
下面的是<
它是利用了抖动技术显示了8bit和24bit的位图(也就是256色和16M色位图),应该能满足你的需要.
不过,我想问下,你老师教过抖动显示吗?
#include
#include
#include
#include
#define NoError 0
#define ErrorFileOpen 1
#define ErrorFileType 2
#define ErrorImageColor 3
typedef struct tagBITMAPFILEHEADER
{
unsigned int bfType;
unsigned long bfSize;
unsigned int bfReserved1;
unsigned int bfReserved2;
unsigned long bfoffBits;
}BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER
{
unsigned long biSize;
unsigned long biWidth;
unsigned long biHeight;
unsigned int biPlanes;
unsigned int biBitCount;
unsigned long biCompression;
unsigned long biSizeImage;
unsigned long biXPelsPerMeter;
unsigned long biYPelsPerMeter;
unsigned long biClrUsed;
unsigned long biClrImportant;
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD
{
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
unsigned char PalReg[17]= { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0}; unsigned char StandardPal[48]= {
0, 0, 0, 32, 0, 0, 0,32, 0, 32,32, 0, 0, 0,32, 32, 0,32, 0,32,32, 32,32, 32, 48, 48,48, 63, 0, 0, 0,63, 0, 63,63, 0, 0, 0,63, 63, 0,63, 0,63,63, 63,63,63, };
unsigned char LightnessMatrix [16][16]= {
{ 0,235,59,219,15,231,55,215,2,232,56,217,12,229,52,213},
{128,64,187,123,143,79,183,119,130,66,184,120,140,76,180,116},
{33,192,16,251,47,207,31,247,34,194,18,248,44,204,28,244},
{161,97,144,80,175,111,159,95,162,98,146,82,172,108,156,92},
{8,225,48,208,5,239,63,223,10,226,50,210,6,236,60,220},
{136,72,176,112,133,69,191,127,138,74,178,114,134,70,188,124},
{41,200,24,240,36,197,20,255,42,202,26,242,38,198,22,252},
{169,105,152,88,164,100,148,84,170,106,154,90,166,102,150,86},
{3,233,57,216,13,228,53,212,1,234,58,218,14,230,54,214},
{131,67,185,121,141,77,181,117,129,65,186,122,142,78,182,118},
{35,195,19,249,45,205,29,245,32,193,17,250,46,206,30,246},
{163,99,147,83,173,109,157,93,160,96,145,81,174,110,158,94},
{11,227,51,211,7,237,61,221,9,224,49,209,4,238,62,222},
{139,75,179,115,135,71,189,125,137,73,177,113,132,68,190,126},
{43,203,27,243,39,199,23,253,40,201,25,241,37,196,21,254},
{171,107,155,91,167,103,151,87,168,104,153,89,165,101,149,85},
};
unsigned char ColorTable[2][2][2]= {{{0,12},{10,14}},{{9,13},{11,15}}}; unsigned char ColorMap[256][3];
int ShowBmp(char *FileName);
int GetColor(unsigned char R,unsigned char G, unsigned char B,int X,int Y); void SetVideoMode(unsigned char Mode);
void SetPalReg(unsigned char *palReg);
void SetDacReg(unsigned char *DacReg, int Color, int Count);
void PutPixel(int X, int Y, unsigned char Color);
/* 主函数*/
void main (int argc, char *argv[])
{
if(argc!=2)
{
printf("Usage:\tSHOW Filename.BMP\n");
exit(1);
}
ShowBmp(argv[1]);
}
/* 根据图像文件名,读取图像内容并利用抖动技术进行显示*/ int ShowBmp(char *FileName)
{
FILE *Fp;
BITMAPFILEHEADER FileHead;
BITMAPINFOHEADER InfoHead;
RGBQUAD RGB;
int N, W,Y,X,C,Color;
unsigned char Buffer[4096];
Fp=fopen(FileName,"rb");
if (Fp==NULL)
return(ErrorFileOpen);
fread(&FileHead,sizeof(BITMAPFILEHEADER),1,Fp);
if(FileHead.bfType!='BM')
{
fclose(Fp);
return(ErrorFileType);
}
fread(&InfoHead,sizeof(BITMAPINFOHEADER),1,Fp);
if(InfoHead.biBitCount!=8 && InfoHead.biBitCount!=24)
{
fclose(Fp);
return(ErrorImageColor);
}
/* 设置显示模式和显示区域*/
SetVideoMode(0x12);
SetPalReg(PalReg);
SetDacReg(StandardPal,0,16);
/* 对两种不同色彩数的图像分别进行处理*/
if(InfoHead.biBitCount==8) /* 256色*/
{
for (N=0;N<256;N++)
{
fread(&RGB, sizeof(RGBQUAD),1,Fp);
ColorMap[N][0]=RGB.rgbRed;
ColorMap[N][1]=RGB.rgbGreen;
ColorMap[N][2]=RGB.rgbBlue;
}
W=(InfoHead.biWidth+3)/4*4;
for(Y=InfoHead.biHeight-1;Y>=480;Y--)
fread(Buffer,sizeof(unsigned char),W,Fp);
for(;Y>0;Y--)
{
fread(Buffer,sizeof(unsigned char),W,Fp);
for (X=0;X { C=Buffer[X]; Color=GetColor(ColorMap[C][0],ColorMap[C][1],ColorMap[C][2],X,Y); PutPixel (X,Y,Color); } } } else /* 24bits真彩色*/ { W=(InfoHead.biWidth*3+3)/4*4; for(Y=InfoHead.biHeight-1;Y>639;Y--) fread(Buffer,sizeof(unsigned char),W,Fp); for(;Y>=0;Y--) { fread(Buffer,sizeof(unsigned char),W,Fp); for(X=0;X { C=X*3; Color=GetColor(Buffer[C+2],Buffer[C+1],Buffer[C],X,Y); PutPixel(X,Y,Color); } } } getch(); fclose(Fp); SetVideoMode(0x03); return(NoError); } int GetColor(unsigned char R, unsigned char G, unsigned char B, int X, int Y) { unsigned int L=LightnessMatrix[Y & 0x0F][X & 0x0F]; return(ColorTable[(unsigned int)R*256/255>L][(unsigned int)G*256/255>L][(unsigned int)B*256/255>L]); } void SetVideoMode(unsigned char Mode) { _AH=0x00; _AL=Mode; geninterrupt(0x10); } void SetPalReg(unsigned char *PalReg) { _ES=FP_SEG((unsigned char far*)PalReg); _DX=FP_OFF((unsigned char far*)PalReg); _AX=0x1002; geninterrupt(0x10); } void SetDacReg(unsigned char *DacReg,int Color,int Count) { _ES=FP_SEG((unsigned char far*)DacReg); _DX=FP_OFF((unsigned char far*)DacReg); _AX=0x1012; _BX=Color; _CX=Count; geninterrupt(0x10); } /* 在对应位置显示像素色彩*/ void PutPixel(int X, int Y, unsigned char Color) { _AH=0x0C; _AL=Color; _CX=X; _DX=Y; geninterrupt(0x10); } 16色位图的显示 文:吴进/Luckylai 对于象大家常用TC的16色图形模式编程的初学者,如果能在程序里使用图片那就会方便很多了,以前在TC256上看见吴进写的《TC的16色BMP闪电显示(66k) 》的代码,发现写的的确不错,而且绝对能在TC的initgraph()初始化的BGI模式下使用。美中不足是会产生中间文件。后来想了一下,干脆就使用那个中间文件,用程序把BMP转换成中间文件,直接用putimage输出不就好了。说到这里,可能大家还不能明白,不要紧下面我慢慢说起。 (一)吴进的《TC的16色BMP闪电显示》 Turbo C2.0图形模式下显示BMP位图的高速方法,不同于一般的putpixel和line绘制的方法,速度之快令你感觉不到显示的过程,而且绝对能在TC的initgraph()初始化的BGI模式下使用。 /*程序设计:吴进*/ /*注意:编译程序时内存模式必需为 Large 以上模式*/ #include "stdio.h" #include "dir.h" #include "dos.h" #include "graphics.h" char *malloc();/*malloc转换*/ char bmp_to_dat(char *bmp,char *dat) /*将16色BMP文件转换为可以用putimage输出的格式,bmp为原BMP文件,dat为转化文件*/ {unsigned char c[8],scan_times,scan_pixs; unsigned char workpos;int i,j,k,n,nowpos,iw,ih; static int color[16]={0,4,2,6,1,5,3,7,8,12,10,14,9,13,11,15}; unsigned char workline[640],scanline[640]; FILE *fp,*targetfp; union {unsigned char value; struct {unsigned cl:4; unsigned ch:4; }color; }mycolor; if((fp=fopen(bmp,"rb"))==NULL)return(0); targetfp=fopen(dat,"wb"); fseek(fp,18,SEEK_SET); iw=0;ih=0; fread(&iw,4,1,fp); /*读图像宽度*/ fread(&ih,4,1,fp); /*读图像高度*/ if(iw==0&&ih==0&&iw>640&&ih>480) {fclose(fp);fclose(targetfp);return(0);} iw--;ih--; /*∵putimage中的长宽比实际数值少1*/ scan_times=iw/8+1; /*行处理单位数*/ scan_pixs=scan_times*4; /*行像素字节数∵1单位=4字节*/ fputc(iw%256,targetfp); /*填充信息头:长、宽部分*/ fputc(iw/256,targetfp); fputc(ih%256,targetfp); fputc(ih/256,targetfp); fseek(fp,-scan_pixs,SEEK_END); for(j=0;j<=ih;j++) {nowpos=0; fread(scanline,scan_pixs,1,fp); fseek(fp,-scan_pixs*2,SEEK_CUR); for(n=3;n>=0;n--) /*解码4个位面*/ {for(i=0;i for(k=0;k<4;k++) /*分离出8个像素*/ {mycolor.value=scanline[i*4+k]; c[k*2]=color[mycolor.color.ch]; c[k*2+1]=color[mycolor.color.cl]; } for(k=0;k<8;k++)workpos+=(c[k]>>n&1)<<(7-k); workline[nowpos]=workpos;nowpos++; }} fwrite(workline,scan_pixs,1,targetfp); } fclose(fp);fclose(targetfp); return(1); } main() {int gd=VGA,gm=VGAHI,n; char *buffer,bmpfile[13],_16file[13]={0}; FILE *fp; struct ffblk *ff; initgraph(&gd,&gm,""); puts("(c)2001 BY Wu Jin"); printf("Now begin showing 16 colors BMP...\n\n"); puts("Select 16 color BMP file to put:"); printf("0. Exit\n1. zx.bmp\n2. mm.bmp\n3.Other bmp file(*.bmp In this directory)\n"); while(1) {n=getch(); switch(n) {case 0 :continue; case '0':exit(0); case '1':strcpy(bmpfile,"zx.bmp");goto OUT; case '2':strcpy(bmpfile,"mm.bmp");goto OUT; case '3':{printf("Input filename(*.bmp):"); gets(bmpfile);goto OUT; } } } OUT: strncpy(_16file,bmpfile,strlen(bmpfile)-3); strcat(_16file,"dat"); if(!bmp_to_dat(bmpfile,_16file)) {puts("File Error!");closegraph();exit(0);} fp=fopen(_16file,"rb"); findfirst(_16file,ff,FA_ARCH); if((buffer=malloc(ff->ff_fsize))==NULL)exit(0); n=0; while(!feof(fp)) {buffer[n]=fgetc(fp); n++; } for(n=0;n<100;n++) {putimage(350-n,n/2,buffer,COPY_PUT); } getch(); closegraph(); free(buffer); fclose(fp); } 上面的程序没有点基本工也看不懂。我来解释一下。先调用bmp_to_dat()把BMP转换成TC可以直接putimage输出的格式的文件,宽展名DA T。至于MAIN函数不用管太多,有价值的就一句putimage(350-n,n/2,buffer,COPY_PUT);输出而已。 (二)改进 由于吴进的程序是在输出图片前进行格式转换,因此时间都耗在转换中了。而我使用转换程序把BMP先转换完毕。而随程序发布的就只有中间文件了(暂时命为PIC文件)。 下面是转换程序。(其实就是照搬吴进的程序,捎做改进) /*程序设计:luckylai*/ /*文件名:bmp2pic.c */ #include "stdio.h" #include "dir.h" #include "dos.h" char bmp_to_dat(char *bmp,char *dat) /*将16色BMP文件转换为可以用putimage输出的格式,bmp为原BMP文件,dat为转化文件*/ {unsigned char c[8],scan_times,scan_pixs; unsigned char workpos;int i,j,k,n,nowpos,iw,ih; static int color[16]={0,4,2,6,1,5,3,7,8,12,10,14,9,13,11,15}; unsigned char workline[640],scanline[640]; FILE *fp,*targetfp; union {unsigned char value; struct {unsigned cl:4; unsigned ch:4; }color; }mycolor; if((fp=fopen(bmp,"rb"))==NULL)return(0); targetfp=fopen(dat,"wb"); fseek(fp,18,SEEK_SET); iw=0;ih=0; fread(&iw,4,1,fp); /*读图像宽度*/ fread(&ih,4,1,fp); /*读图像高度*/ if(iw==0&&ih==0&&iw>640&&ih>480) {fclose(fp);fclose(targetfp);return(0);} iw--;ih--; /*∵putimage中的长宽比实际数值少1*/ scan_times=iw/8+1; /*行处理单位数*/ scan_pixs=scan_times*4; /*行像素字节数∵1单位=4字节*/ fputc(iw%256,targetfp); /*填充信息头:长、宽部分*/ fputc(iw/256,targetfp); fputc(ih%256,targetfp); fputc(ih/256,targetfp); fseek(fp,-scan_pixs,SEEK_END); for(j=0;j<=ih;j++) {nowpos=0; fread(scanline,scan_pixs,1,fp); fseek(fp,-scan_pixs*2,SEEK_CUR); for(n=3;n>=0;n--) /*解码4个位面*/ {for(i=0;i {workpos=0; for(k=0;k<4;k++) /*分离出8个像素*/ {mycolor.value=scanline[i*4+k]; c[k*2]=color[mycolor.color.ch]; c[k*2+1]=color[mycolor.color.cl]; } for(k=0;k<8;k++)workpos+=(c[k]>>n&1)<<(7-k); workline[nowpos]=workpos;nowpos++; }} fwrite(workline,scan_pixs,1,targetfp); } fclose(fp);fclose(targetfp); return(1); } void main(int argc,char *aaaa[]) { int i;char a1[30],a2[30]; printf("\n\nUsage: Bmp2pic -inputfile [*.bmp] -outputfile[*.pic]\ncopy right (C) 1989-2002 CEN TER COMPUTER CO, LTD.\n"); if(aaaa[1]==NULL) { printf("\nInput Filename:"); gets(a1); aaaa[1]=a1; // if(aaaa[2]==NULL) printf("\nOutput Filename:"); gets(a2); aaaa[2]=a2; } i=bmp_to_dat(aaaa[1],aaaa[2]); if(i==0) printf("Fail"); } 之后的事情就很简单了。 输出函数 #include "stdio.h" #include "dir.h" #include "dos.h" int Pic_Putout(int x,int y,char *filenames) {int n; char *buffer; FILE *fp; struct ffblk *ff; fp=fopen(filenames,"rb"); //打开PIC文件 findfirst(filenames,ff,FA_ARCH);//获得文件信息,主要是大小 if((buffer=malloc(ff->ff_fsize))==NULL)exit(0); putimage(x,y,buffer,COPY_PUT); //输出 free(buffer); fclose(fp); } 如果你要频繁使用这张图片,那么就只在初始化时打开一次文件,内存不要释放,请读者自己写好了。 (四)图片处理的问题 16色图片由于颜色信息少,因此处理上就得很讲究了。使用xnview114gb.exe这个免费软件吧(本站可以下载),功能强大。另外的他的抖动处理,可以使16色图片显示更多信息。另外一个要点就是调色板的问题。使用ACDSEE等软件转换出来的16色,并不是标准的16色。他们使用了调色板。为了正确显示,建议使用WINDOWS自带的“画图”软件,进行处理。 /*********************************************** *******在DOS下显示16位色位图源程序************** ***********************************************/ #include #include #include #include #include #include #include #include #include #include #define closegr closegraph /*********************************************** *************位图文件头结构********************* ***********************************************/ typedef struct { int id; /*两字节的内容用来识别位图的类型: 'BM': Windows 3.1x, 95, NT, … 'BA':OS/2 Bitmap Array 'CI':OS/2 Color Icon 'CP':OS/2 Color Pointer 'IC': OS/2 Icon 'PT':OS/2 Pointer 注:因为OS/2系统并没有被普及开,所以在编程时,你只需判断第一个标识"BM"就行 */ long filesize; /*用字节表示的整个文件的大小*/ long reserved; /*保留,必须设置为0*/ long dataoffset; /*从文件开始到位图数据开始之间的数据(bitmap data)之间的偏移量*/ long headersize; /*位图信息头(Bitmap Info Header)的长度,用来描述位图的颜色、压缩方法等。下面的长度表示: 28h - Windows 3.1x, 95, NT, … 0Ch - OS/2 1.x F0h - OS/2 2.x 注:在Windows95、98、2000等操作系统中,位图信息头的长度并不一定是28h,因为微软已经制定出了新的BMP文件格式,其中的信息头结构变化比较大,长度加长。所以最好不要直接使用常数28h,而是应该从具体的文件中读取这个值。这样才能确保程序的兼容性。*/ long width; /*位图的宽度,以象素为单位*/ long height;/*位图的高度,以象素为单位*/ int Planes; /*位图的位面数(注:该值将总是1)*/ int Pixe; /*每个象素的位数 1 - 单色位图(实际上可有两种颜色,缺省情况下是黑色和白色。你可以自己定义这两种颜色) 4 - 16 色位图 8 - 256 色位图 16 - 16bit 高彩色位图 24 - 24bit 真彩色位图 32 - 32bit 增强型真彩色位图 */ long Compression; /*压缩说明: 0 - 不压缩 (使用BI_RGB表示) 1 - RLE 8-使用8位RLE压缩方式(用BI_RLE8表示) 2 - RLE 4-使用4位RLE压缩方式(用BI_RLE4表示) 3 - Bitfields-位域存放方式(用BI_BITFIELDS表示) */ long bmpDataSize; /*用字节数表示的位图数据的大小。该数必须是4的倍数*/ long XPelsPerMeter;/*用象素/米表示的水平分辨率*/ long YPelsPerMeter;/*用象素/米表示的垂直分辨率*/ long ClrUsed; /*位图使用的颜色数。如8-比特/象素表示为100h或者 256*/ long ClrImportant; /*指定重要的颜色数。当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要*/ }BMPHEAD; BMPHEAD BmpHead; char convert[16] = {0x0,0x4,0x2,0x6,0x1,0x5,0x3,0x7,0x8,0xc,0xa,0xe,0x9,0xd,0x3,0xf};/*BM P色彩与VGA色对照表*/ unsigned char * bmp_data; int fp; /********************************************** ************ BGI初始化函数********************* **********************************************/ void initgr(void) { int gd = DETECT, gm = 0; /* 和gd = VGA,gm = VGAHI是同样效果 */ registerbgidriver(EGA VGA_driver);/* 注册BGI驱动后可以不需要.BGI文件的支持运行 */ initgraph(&gd, &gm, ""); } /*********************************************** *************位图文件头读取函数***************** ***********************************************/ int read_bmp_head(char *bmp_filename) { if((fp=open(bmp_filename,O_RDONL Y))==-1)/*打开位图文件*/ { printf("%s%s",bmp_filename," is not found."); return 1; } read(fp,&BmpHead,sizeof(BMPHEAD));/*读取BMP文件的信息头*/ if(BmpHead.id!=0x4d42) /*判断是否是BMP文件*/ { printf("%s%s","'",bmp_filename,"' is not BMPfile"); return 1; } return 0; } /*********************************************** **************纠正宽度函数********************** ***********************************************/ int correct_width(int width) { int factual_width; if(width%4==0&&(width/4)%2==0); else while(width%4!=0||(width/4)%2!=0)width++; factual_width=width; return factual_width; } /*********************************************** *************读位图文件并显示函数*************** ***********************************************/ void put_bmp(char * bmpfile,int x,int y) { int read_bmp_head_return,i,j,d=0,cn; bmp_data=(unsigned char *)malloc(BmpHead.bmpDataSize*sizeof(unsigned char)); if((read_bmp_head_return=read_bmp_head(bmpfile))==1) { getch(); exit(1); } lseek(fp,BmpHead.dataoffset,SEEK_SET); read(fp,bmp_data,BmpHead.bmpDataSize);/*读取颜色数据到缓冲区内*/ cn=correct_width(BmpHead.width); for(i=BmpHead.height;i>0;i--) for(j=0;j { putpixel(x+j++,y+i,convert[bmp_data[d]>>4]);/*用高4位画1个点*/ putpixel(x+j++,y+i,convert[bmp_data[d]&0xf]); /*用低4位画1个点*/ d++; } free(bmp_data); close(fp); } /*********************************************** *************主函数***************************** ***********************************************/ int main(void) { initgr(); put_bmp("pix.bmp",320,240); getch(); closegr(); return 0; } /* 这是上面程序的一个应用 XP WIN2000 可以不用再驱动鼠标了 */ #include "Conio.h" #include #include #include "graphics.h" #define closegr closegraph union REGS regs; void initgr(void) /* BGI初始化 */ { int gd = DETECT, gm = 0; /* 和gd = VGA,gm = VGAHI是同样效果 */ registerbgidriver(EGA VGA_driver);/* 注册BGI驱动后可以不需要.BGI文件的支持运行 */ initgraph(&gd, &gm, ""); } int initmouse(int xmin,int xmax,int ymin,int ymax) { int retcode; regs.x.ax=0; int86(0x33,?s,?s); retcode=regs.x.ax; if(retcode==0)/*ax=0 安装失败*/ return 0; regs.x.ax=7; regs.x.cx=xmin; regs.x.dx=xmax; int86(0x33,?s,?s); /* 设置水平位置最大最小值*/ regs.x.ax=8; regs.x.cx=ymin; regs.x.dx=ymax; int86(0x33,?s,?s); /* 设置垂直位置最大最小值*/ return retcode; } int read(int *mx,int *my,int *mbutt) /*对鼠标的位置和按键状态函数*/ { int x0=*mx,y0=*my,buto=0; int xnew,ynew; do { regs.x.ax=3; int86(0x33,?s,?s);/* 取按键状态及鼠标位置 */ xnew=regs.x.cx; ynew=regs.x.dx; *mbutt=regs.x.bx; }while(xnew==x0&&ynew==y0&&*mbutt==buto);/*等到状态改变*/ if(*mbutt>>2<<7) { *mx=xnew; *my=ynew; return 2; /*中键按下*/ } else if(*mbutt>>1<<7) { *mx=xnew; *my=ynew; return 1; /*右键按下*/ } else if(*mbutt<<7) { *mx=xnew; *my=ynew; return -1; /*左键按下*/ } else { *mx=xnew; *my=ynew; return 0; /*无键按下*/ } } void cursor(int x,int y,char *pic) /*画鼠标*/ { put_bmp(pic,x,y); } int mouse_move(int *x,int *y,int *button) { int x0=*x,y0=*y,mouse_button; mouse_button=read(x,y,button); cursor(x0,y0,"pix.bmp"); cursor(*x,*y,"pix.bmp"); return mouse_button; } int mouse_bound(int *x,int *y,int m1,int n1,int m2) { if(m1<*x&&*x { if(n1<*y&&*y } return 1; } /**************************************************************** ***************************************************************** ***************************************************************** 把上面的函数加进来 ***************************************************************** *****************************************************************/ int main(void) { int init_mouse_x=320,init_mouse_y=240,buttons,mouse_button,bound_betton; int bound_x,bound_y; initgr(); /* BGI初始化 */ if(initmouse(0,639,0,479)==0) /* 鼠标初始化 */ { /* 未安装鼠标处理*/ exit(1); } else { cursor(init_mouse_x,init_mouse_y,"pix.bmp"); do { mouse_button=mouse_move(&init_mouse_x,&init_mouse_y,&buttons); bound_x=init_mouse_x;bound_y=init_mouse_y; if(mouse_button==-1){ bound_betton=mouse_bound(&bound_x,&bound_y,300,390,340); if(bound_betton==0)break; } }while(1); } closegr(); /* 恢复TEXT屏幕模式 */ return 0; } 近来,图像功能在计算机上的应用十分广泛,一种方便快捷而实用的方法是首先利用图像扫描仪将图像数据自动生成并存入计算机,再利用Windows的PAINTBRUH功能进行加工修改,成为独立的图像文件。但是,仍然有一个问题:PAINTBRUH软件只能在Windows环境下运行。因此要显示一幅图像(.MSP位图、.BMP位 图、PCX位图),也要运行庞大的Windows,显然不方便。为此,笔者编写了一个BMP位图文件直接显示在屏幕上的程序。利用Turbo C位图格式,将BMP格式进行转换,实现了这一功能。 一、Turbo C位图格式 Borlond公司的Turbo C是目前在微机上最为流行的C语言版本。它为软件开发者提供了丰富的屏幕操作与图形功能函数。其中getimage( )函数用于将屏幕内的某矩形区域复制到内存缓冲区,putimage( )函数将内存缓冲区中的内容再复制到屏幕上。但由于图像事先还没有出现在屏幕上,所以,不能使用getimage( )函数填写供putimage( )函数显示的内存图像数据。不过可以把图像数据按getimage( )函数产生的格式填于内存缓冲区中,然后调用putimage( )函数,显示内存缓冲区图像,下面分析getimage( )函数产生的格式: getimage (int letf, int top, int night, int bottm, *buf) 其中,left, top——矩形区域左上角坐标(x,y)。 right, bottom——矩形区域右上角坐标(x,y)。 * buf——指向存储屏幕数据的内存指针。 在内存中,图像数据是按行存放的。头两个字节为图像的宽度,接下去两个字节为图像的高度(均为低字节在前,高字节在后)。后面是真正的图像数据,它以图像的宽度为单位,先是图像的第一行第三位面的内容,然后是第一行第二位面的内容;第一行第一位面的内容;第一行第零位面的内容。第一行完后,接第二行的四个位面,第三行的四个位面等等。当屏幕状态为16色时,则需4个彩色页面。这时,如果图像的宽度为8的整数倍时,则每 行所需的字节数为: number=(right-left+8) 18*4 如果图像的宽度不为8的倍数据时,则 number=(ceil ((right-left)/8)*4) 其中,ceil (righ-left)8为取大于(right-left)/8的最小整数 则图像数据所需字节总数为: number * (bottom-top+1) 则所需内存缓冲区字节总数为: 6+number * (bottom -top+1) 这是因为头四个字节存放图像的宽和高,而缓冲区最后2个字节无意义,可以填零。 @@T5S04700.GIF;图 1@@ 这样,每行每页面的字节数为: p=number/4 如图1所示(设宽、高为16×16) 其中,图像的真正数据在每个页面的前m列,即: 当宽度为8的倍数据时,m=P-1,此时每页面的最后一列可为零。而当宽度不为8的倍 数时,m=P。 结合图1 m=2,即数据在每页面的前2列。 二、BMP位图格式 MS Windows的.BMP图像文件可以表示单色或直至24位的彩色图像,.BMP文件是与设备无关的。.BMP文件分为文件首部和文件体两部分。文件首部描述文件和图像的有关参数和彩色表,主体是图像的位图数据。.BMP文件的格式如表1所示。 @@T5S04701.GIF;表1 BMP文件的格式@@ @@T5S04702.GIF;表2 BMP与VGA彩色编码对照@@ .BMP文件的文件体记录图像的位图数据。从图像的左下角开始依次扫描每根扫描线。对于单色,每个象素用1位表示;16色用4位表示一个象素,256色用一个字节,而24位彩色用3个字节。 但.BMP位图的彩色编码与VGA彩色编码不同,必须经过转换才能用C语言在屏幕上显示。表2给出了.BMP 位图彩色编码与VGA彩色编码的对照。 三、.BMP位图向Turbo C位图的转换 当VGA视频内存在选择640×480模式时占用4个位面。每个位面上的一位对应一个点。而.BMP位图则采用压缩法的组织形式,两者显然不同。因此,也需要一个变换过程,见图2。 根据这一原理,首先进行彩色编码转换,然后将.BMP位图数转换成C位图格式,写入内存缓冲区的适当位置。@@T5S04703.GIF;图2 从BMP位图到VGA视频内存的变换过程@@ 为了便于参考,笔者编制了一个程序。本程序只要给出.BMP文件的名字,程序会自行在屏幕上显示出来。需说明的是,本程序只能用于小于64K的图像。但也可以将一幅大的.BMP图像分成若干小图像即可。对图像文件格式转换感兴趣的读者,也不难将本程序拓展到自己的应用领域。程序附后。 # include # include # include # include unsigned char convert (); main() { int 1, i, j, k, q, h; int driver=VGA, mode=VGAHI; int lenth, high, number, p, m; unsigned size; unsigned char ch, a[8], b[4], e[4][8]; FILE *fp; char *s; fp=fopen (“party.bmp” , “rb”); fseek (fp, 18, 0); lenth=getw (fp); /* graphics width */ fseek (fp, 22, 0); high=getw (fp); #/* graphics high */ #if ((lenth%8)==0) number= (lenth+8)/8*4 #/* bytes of each line */ # else number=4*(ceil ((double)1enth/8)); up=number/4; mif ((lenth%8)!=0) m=p; {/* bytes of each line/4 */ else m=p-1; size=6+number*(high+1) /* need memory bytes of graphics */ if ((s=char *) malloc (size))==NULL) { printf (“malloc error !\n”); exit (0);} s[0]=lenth & 0x00ff; s[1]=(lenth& 0xff00)>>8; s[2]=high & 0x00ff; s[3]=(high & 0xff00)>>8; for (i=4; i fseek (fp, 118,0); #for (l=high-1; 1>=0; 1--) #for (q=0; q # for (k=0; k<4; k++) { #ch=getc (fp); uch=convert (ch); ma[0]=ch&0x1; a[1]=(ch&0x2)>>1; {a[2]=(ch&0x4)>>2;a[3]=(ch&0x8)>>3; a[4]=(ch&0x10)>>4;a[5]=(ch&x20)>>5; a[6]=(ch&0x40)>>6;a[7]=(ch&0x80)>>7; h=7; for (j=0; j<2; i++) for (j=0; j<4; i++) { e[j][i+2*k]=a[h]; h--; } } #for (i=0; i<4; i++) { # b[i]=(e[i][0]<<7)+(e[i][1]<<6)+(e[i][2]<<5)+ #(e[i][3]<<4)+(e[i][4]<<3)+(e[i][5]<<2)+ #(e[i][6]<<1)+(e[i][7]); u s[4+number *1+q]=b[0]; s[4+number *1+q+p]=b[1]; m s[4+number *1+q+2*p]=b[2]; s[4+number *1+q+3*p]=b[3]; { } fclose (fp); initgraph (&driver, &mode, “d:\\tc”); putimage (0, 0, s, 0); getch(); free(s): closegraph(); } unsigned char convert (unsigned char ch) /* bmp color convert vga color */ { unsigned char c[2]; int i; #c[0]=(ch&0xf0)>>4; #c[1]=ch&0x0f; #for (i=0; i<2; i++) { #if (c[i]==0x01) {c[i]=0x04; continue;} uif (c[i]==0x03) {c[i]=0x6; continue;} mif (c[i]==0x04) {c[i]=0x01; continue;} {if (c[i]==0x06) {c[i]=0x03; continue;} if (c[i]==0x07) {c[i]=0x8; continue;} if (c[i]==0x08) {c[i]=0x07; continue;} if (c[i]==0x09) {c[i]=0xc; continue;}