vc读入位图:将位图数据读入离屏表面



以下是个人见解请正多谢新浪网朋友们帮助 下面是我刚学会.
在网上看到了许多有关装位图装载到离屏表面文章但是都是使用了WIN32虽然有效但不是很通用如果我们要装载其它格式文件使用不了WIN32不就无能为力了吗?于是我想直接操作文件直接读取位图文件数据到离屏表面网上还是有这样文章但是很少并且没有过多介绍说明其实装载文件到离屏表面也很简单不需要什么算法知识就可以完成主要思路方法是(以256色位图为准): 1、将位图文件颜色表和图像数据读入内存
2、创建前后表面离屏表面
3、根据颜色表创建调色板
4、将位图数据传输到离屏表面
5、利用后表面BLTFAST将图像输出到后表面
6、翻转表面将图像显示到屏幕

先声明几个变量:
BITMAPFILEHEADER bmfh; //位图文件头
BITMAPINFOHEADER bmih; //位图信息头
RGBQUAD rgb[256]; //颜色表

首先我了解了下位图文件结构其实也是很简单
1、文件头:
typedef struct tagBITMAPFILEHEADER{
UINT bfType; //文件标志
DWORD bfSize; //文件大小
UINT bfReserved1,bfReserved2;
DWORD bfOffBits; //数据偏移
}BITMAPFILEHEADER;

bfType:是位图文件标志为\"BM\"当你从文件中读出放到个变量中时它是\"MB\"即0x4d42在内存中存放是高位在前所以在磁盘上是\"BM\"读到内存就为\"MB\"了不信?你个字节个字节将这两个字节存到两个型变量char ch1,ch2;中你会发现ch1\'B\',ch2\'M\'

bfSize:位图文件大小等于位图文件头+信息头+颜色表+位数据以字节为单位即:(bmfh)+(bmih)+(RGBQUAD)*256+bmih.biSizeImage

bfOffBits:位图数据偏移.如果你想直接读取位图数据.使用

fseek(fil_ptr,bmfh.bfOffBits,SEEK_SET);

这样可以直接将文件指针指向位图数据开始地方

2、信息头:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; //信息头大小40字节
LONG biWidth,biHeight; //位图实际宽、高度
WORD biPlanes; //
WORD biBitCount; //位图每像素位数
DWORD biCompression; //
DWORD biSizeImage; //位数据大小(字节)
LONG biXPelsPerMeter,biYPelsPerMeter; //
DWORD biClrUsed; //
DWORD biClrImprotant; //
}BITMAPINFOHEADER;

介绍些重要部分:
biSize:信息头大小即此结构大小为40字节(bmih)

biWidth,biHeight:位图像素宽、高

biBitCount:位图每像素位数指定了这个位图文件颜色深度在这个例子中是8

biSizeImage:位图数据区大小

这里有需要注意:位图数据每行是以4字节增充如果是个256色位图像素占字节如果图像宽度为80像素则图像每行为80字节如果图像宽度为79像素则磁盘上位图文件仍然是80字节(78,77像素每行也为80字节)图像每行76,75,74,73像素则它在文件中占76个字节因此在从磁盘读出数据时要知道每行字节数这里我使用
bytperlin=bmih.biSizeImage/bmih.biHeight;
当然还有其它思路方法可用不过上面比较简单

文件头、信息头的后便是颜色表和位数据了很简单我就不介绍了

的后是从磁盘位图文件中读取数据
首先是位图文件头信息头使用如下语句便可:

fread(&bmfh,(bmfh),1,fil_ptr);
fread(&bmih,(bmih),1,fil_ptr);

这时两个结构bmfh,bmih就存放了我们需要些有关位图信息了
的后再读入颜色表:

RGBQUAD* prgb;
prgb=(RGBQUAD*)malloc((RGBQUAD)*bmih.biBitCount);
//注意在使用指针的前定要给它分配内存空间否则会退出
fread(prgb,(RGBQUAD),1<<bmih.biBitCount,fil_ptr);
这样我们需要颜色值放入了动态分配内存空间了但是在DD中设置调色板话需要是PALETTEENTRY结构因此我们要得到PALETTEENTRY结构数据

PALETTEENTRY* ppal=(PALETTEENTRY*)malloc((PALETTEENTRY)*bmih.biBitCount);
for( i=0;i<(1<<bmih.biBitCount);i)
{
ppal[i].peRed=prgb[i].rgbRed;
ppal[i].peGreen=prgb[i].rgbGreen;
ppal[i].peBlue=prgb[i].rgbBlue;
ppal[i].peFlag=0;
}

的后我们就可以通过这个结构来取得LPDIRECTDRAWPALETTE接口指针的后为前表面设置调色板(在后面中)

最后是读入位图位数据了每个像素是1个字节位图图像数据大小在信息头中已经给定了:bmih.biSizeImage 这个大小包括实际图像数据和为了每行达到4字节而扩充字节数我们用如下思路方法读入:

BYTE* pbuffer=(BYTE*)malloc((BYTE)*bmih.biSizeImage);
//注意定要分配内存空间否则会退出!!!!!!!
fread(pbuffer,(BYTE),bmih.biSizeImage,fil_ptr);

这样位图中数据我们全部读入了
文件头信息头只是给我们提供了个参数方便我们读入位图数据
颜色表用于设置调色板(后面有)
最后是将内存中图像数据传入到离屏表面中了:

DDSURFACEDESC2 ddsd;
ZeroMemory(&ddsd,(ddsd));
ddsd.dwSize=(ddsd); //注意定要化这个结构否则会退出!!
lpDDS_Off->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_WRITEONLY,NULL);
(BYTE*)lpSurf=(BYTE*)ddsd.lpSurface; //取离屏表面指针
//将内存数据复制到离屏表面中
lpDDS_Off->UnLock(NULL);

这样全部事情完成了想要输出离屏表面数据到后表面使用后表面BltFast
就可以了
下面是具体片段:
//给指定表面设置调色板
Create_Palette(LPDIRECTDRAW7 lpDD,LPDIRECTDRAWSURFACE7& lpDDS_Front,LPDIRECTDRAWPALETTE& lpDDP,char* filnam)
{
RGBQUAD rgbquad[256];
PALETTEENTRY pal[256];
(filnam\"\") 0;
//从文件中读入调色板索引
FILE* fil_ptr;
fil_ptr=fopen(filnam,\"rb\");
(fil_ptrNULL) 0;
fseek(fil_ptr,(BITMAPFILEHEADER)+(BITMAPINFOHEADER),SEEK_SET);
fread(rgbquad,(RGBQUAD),256,fil_ptr);
fclose(filnam);
for( i=0;i<256;i)
{


pal[i].peBlue=rgbquad[i].rgbBlue;
pal[i].peGreen=rgbquad[i].rgbGreen;
pal[i].peRed=rgbquad[i].rgbRed;
pal[i].peFlags=PC_NOCOLLAPSE;
}
lpDD->CreatePalette(DDPCAPS_8BIT,pal,&lpDDP,NULL);
lpDDS_Front->SetPalette(lpDDP);

1;
}


//装载位图数据在这个创建离屏表面(Init_Off)
LoadData_8(char* filnam)
{
(filnam\"\") 0;

FILE* fil_ptr;
fil_ptr=fopen(filnam,\"rb\");
(fil_ptrNULL) 0;

BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;

fread(&bmfh,(bmfh),1,fil_ptr);
fread(&bmih,(bmih),1,fil_ptr);

Init_Off(lpDD,lpDDS_Off,bmih.biWidth,bmih.biHeight);

bytperlin=bmih.biSizeImage/bmih.biHeight; //位图数据每行字节数

BYTE* pbuffer=NULL;
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
pbuffer=(BYTE*)malloc((BYTE)*bmih.biSizeImage);
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
fseek(fil_ptr,bmfh.bfOffBits,SEEK_SET);
fread(pbuffer,(BYTE),bmih.biSizeImage,fil_ptr);
fclose(fil_ptr);

DDSURFACEDESC2 ddsd;
mem(&ddsd,0,(ddsd));
ddsd.dwSize=(ddsd);
lpDDS_Off->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_WRITEONLY,NULL);
BYTE* pSurf=(BYTE*)ddsd.lpSurface;
BYTE* pData=pbuffer;
pDatabmih.biSizeImage; //指针已经出了数据区,回位,就是数据区.


for( row=0;row<bmih.biHeight;row)
{
pData-=bytperlin;
memcpy(pSurf,pData,bmih.biWidth);
pSurfddsd.lPitch; //表面从第行依次向后定位
}
lpDDS_Off->Unlock(NULL);
1;
}

//给表面设置透明色
SetColorKey8
{
DDSURFACEDESC2 ddsd;
ZeroMemory(&ddsd,(ddsd));
ddsd.dwSize=(ddsd);
lpDDS_Off->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_READONLY,NULL);
BYTE color=*(BYTE*)ddsd.lpSurface;
lpDDS_Off->Unlock(NULL);
DDCOLORKEY ddck;
ddck.dwColorSpaceHighValue=color;
ddck.dwColorSpaceLowValue=color;
lpDDS_Off->SetColorKey(DDCKEY_SRCBLT,&ddck);
1;
}

最后还有几点要特别注意就是在使用DD中结构时候定要事先化好例如:

DDSURFACEDESC2 ddsd;
ZeroMemory(&ddsd,(ddsd));
ddsd.dwSize=(ddsd);

可千万别小看了它如果不设置好它会无故退出
还有个地方如果会无故退出那就是指针
比如我们用于存放位图数据内存指针:
BYTE* lpbuffer;
在使用它的前定要分配内存空间否则也会无故退出
lpbuffer=(BYTE*)malloc((BYTE)*bmih.biImageSize;

上面两点要切记如果无故退出从上面找找看

下面说下位图数据从内存到离屏表面复制问题

首先是:有两个指针指向内存图像数据和离屏表面假设为lpbuffer,lpsurf;
lpbuffer是我们读数据时产生lpsurf是锁定离屏表面时得到
其次是:内存图像数据有个表示每行字节数变量bytperlin
离屏表面有个表示跨度变量ddsd.lPitch;
bytperlin=bmih.biSizeImage/bmih.biHeight;
即每行字节数为图像数据大小除以图像高度
ddsd.lPitch是我们在锁定表面时得到
这两个变量在我们复制数据时用于定位每行数据首位置因此很重要
再次是:位图宽度字节数位图数据被扩充为4字节倍数因此如果位图数据不是4字节倍数就要以0扩充了而我们在向离屏表面复制数据时不应该把这扩充数据复制到离屏表面而是复制位图每行实际宽度字节bytperwid在8位图像数据中bytperwid=bmih.biWidth;在16位图像数据中bytperwid=bmih.biWidth*2;

这就是我要说内容了还不是很清楚请大家原谅



Tags:  什么是位图 表面声波屏 vc读入位图

延伸阅读

最新评论

发表评论