??xml version="1.0" encoding="utf-8" standalone="yes"?>
用普通方法显CBMP位图Q占内存大,速度慢,在图形羃时Q失真严?在低颜色位数的设备上昄高颜色位数的囑Ş囑Ş时失真大。本文采用视频函数显CBMP位图Q可以消除以上的~点?/font>
一、BMP文gl构
BMP文gl成
颜色表用于说明位图中的颜Ԍ它有若干个表,每一个表Ҏ一个RGBQUADcd的结构,定义一U颜艌ӀRGBQUADl构的定义如?
1. 甌内存I间用于存放位图文g
2. 位图文gd所甌内存I间?br /> LoadFileToMemory( mpBitsSrcQmFileName);
3. 在OnPaint{函C用创建显C用位图
4. 用BitBlt或StretchBlt{函数显CZ?/font>
5. 用DeleteObject()删除所创徏的位?/font>
以上Ҏ的缺Ҏ: 1)昄速度? 2) 内存占用? 3) 位图在羃显C时囑Şq?(可通过安装字体qx软g来解?; 4) 在低颜色位数的设备上(?56昄模式)昄高颜色位数的囑Ş(如真彩色)囑Şq严重?/font>
三、BMP位图~放昄
1. 打开视频函数DrawDibOpen()Q一般放在在构造函C
2. 甌内存I间用于存放位图文g
3. 位图文gd所甌内存I间?---
4. 在OnPaint{函C用DrawDibRealize()QDrawDibDraw()昄位图
5. 关闭视频函数DrawDibClose(),一般放在在析构函数?/font>
以上Ҏ的优Ҏ: 1)昄速度? 2) 内存占用? 3) ~放昄时图形失真小,4) 在低颜色位数的设备上昄高颜色位数的囑Ş囑Ş时失真小; 5) 通过直接处理位图数据Q可以制作简单动甅R?/font>
四、CViewBimapcȝE要?/font>
1. 在CViewBimapcMd视频函数{成?/font>
HDRAWDIB m_hDrawDib; // 视频函数
2. 在CViewBimapcL造函Cd打开视频函数
3. 在CViewBimapcL构函Cd关闭视频函数
if( m_hDrawDib != NULL)
4. 在CViewBimapcd形显C函数OnPaint中添加GraphicDraw()
voidCViewBitmap::GraphicDraw( void )
// 囑Ş文g名有?(=0 BMP)
// 囑Ş文g名有?(=0 BMP)
// 使用普通函数显CZ?/font>
if( m_hDrawDib == NULL || mDispMethod == 0)
if( mFullViewTog == 0)
// 使用视频函数昄位图
if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))
// 昄真彩位图
if( mFullViewTog == 0)
DrawDibDraw( m_hDrawDib, dc.GetSafeHdc() 一U位囄攄快速算?amp;PSD格式的开?amp;PSD格式文g的读?/p>
一U位囄攄快速算法?img src="E:%5CBaseInfoImage.bmp" />
]]>
BMP文g由文件头、位图信息头、颜色信息和囑Ş数据四部分组成?
BMP文g?br />BMP文g头数据结构含有BMP文g的类型、文件大和位图起始位置{信息?
其结构定义如?
typedef struct tagBITMAPFILEHEADER
{
WORDbfType; // 位图文g的类型,必须为BM
DWORD bfSize; // 位图文g的大,以字节ؓ单位
WORDbfReserved1; // 位图文g保留字,必须?
WORDbfReserved2; // 位图文g保留字,必须?
DWORD bfOffBits; // 位图数据的v始位|,以相对于位图
// 文g头的偏移量表C,以字节ؓ单位
} BITMAPFILEHEADER;
3. 位图信息?/font>
BMP位图信息头数据用于说明位囄寸{信息?br />typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 本结构所占用字节?br />LONGbiWidth; // 位图的宽度,以像素ؓ单位
LONGbiHeight; // 位图的高度,以像素ؓ单位
WORD biPlanes; // 目标讑֤的别,必须?
WORD biBitCount// 每个像素所需的位敎ͼ必须?(双色),
// 4(16?Q?(256??4(真彩?之一
DWORD biCompression; // 位图压羃cdQ必L 0(不压~?,
// 1(BI_RLE8压羃cd)?(BI_RLE4压羃cd)之一
DWORD biSizeImage; // 位图的大,以字节ؓ单位
LONGbiXPelsPerMeter; // 位图水^分L率,每米像素?br />LONGbiYPelsPerMeter; // 位图垂直分L率,每米像素?br />DWORD biClrUsed;// 位图实际使用的颜色表中的颜色?br />DWORD biClrImportant;// 位图昄q程中重要的颜色?br />} BITMAPINFOHEADER;
4. 颜色?/font>
typedef struct tagRGBQUAD {
BYTErgbBlue;// 蓝色的亮?D围ؓ0-255)
BYTErgbGreen; // l色的亮?D围ؓ0-255)
BYTErgbRed; // U色的亮?D围ؓ0-255)
BYTErgbReserved;// 保留Q必Mؓ0
} RGBQUAD;
颜色表中RGBQUADl构数据的个数有biBitCount来确?
当biBitCount=1,4,8Ӟ分别?,16,256个表?
当biBitCount=24Ӟ没有颜色表项?br />位图信息头和颜色表组成位图信息,BITMAPINFOl构定义如下:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 位图信息?br />RGBQUAD bmiColors[1]; // 颜色?br />} BITMAPINFO;
5. 位图数据
位图数据记录了位囄每一个像素|记录序是在扫描行内是从左到?扫描行之间是从下C。位囄一个像素值所占的字节?
当biBitCount=1Ӟ8个像素占1个字?
当biBitCount=4Ӟ2个像素占1个字?
当biBitCount=8Ӟ1个像素占1个字?
当biBitCount=24?1个像素占3个字?
Windows规定一个扫描行所占的字节数必L
4的倍数(即以long为单?,不的以0填充Q?br />一个扫描行所占的字节数计方?
DataSizePerLine= (biWidth* biBitCount+31)/8;
// 一个扫描行所占的字节?br />DataSizePerLine= DataSizePerLine/4*4; // 字节数必L4的倍数
位图数据的大?不压~情况下):
DataSize= DataSizePerLine* biHeight;
二、BMP位图一般显C方?/font>
GlobalAlloc(GHNDQFileLength);
用CreateDIBitmap()创徏昄用位图,用CreateCompatibleDC()创徏兼容DC,
用SelectBitmap()选择昄位图?/font>
用DrawDib视频函数来显CZ图,内存占用,速度快,而且q可以对囑Şq行淡化(Dithering)处理。E化处理是一U图形算法,可以用来在一个支持比囑փ所用颜色要的讑֤上显C彩色图像。BMP位图昄Ҏ如下:
GlobalAlloc(GHNDQFileLength);
LoadFileToMemory( mpBitsSrcQmFileName);
HANDLEmhBitsSrc; // 位图文g句柄(内存)
LPSTR mpBitsSrc; // 位图文g地址(内存)
BITMAPINFOHEADER *mpBitmapInfo; // 位图信息?/font>
m_hDrawDib= DrawDibOpen();
{
DrawDibClose( m_hDrawDib);
m_hDrawDib = NULL;
}
voidCViewBitmap::OnPaint()
{
CPaintDC dc(this); // device context for painting
GraphicDraw( );
}
{
CClientDC dc(this); // device context for painting
BITMAPFILEHEADER *pBitmapFileHeader;
ULONG bfoffBits= 0;
CPoint Wid;
if( mBitmapFileType < ID_BITMAP_BMP ) return;
// 准备昄真彩位图
pBitmapFileHeader= (BITMAPFILEHEADER *) mpBitsSrc;
bfoffBits= pBitmapFileHeader->bfOffBits;
{
HBITMAP hBitmap=::CreateDIBitmap(dc.m_hDC,
mpBitmapInfo, CBM_INIT, mpBitsSrc+bfoffBits,
(LPBITMAPINFO) mpBitmapInfo,DIB_RGB_COLORS);
// 建立位图
HDC hMemDC=::CreateCompatibleDC(dc.m_hDC);// 建立内存
HBITMAP hBitmapOld= SelectBitmap(hMemDC, hBitmap); // 选择对象
// 成员CRect mDispR用于指示囑Ş昄区域的大?
// 成员CPoint mPos用于指示囑Ş昄起始位置坐标.
if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))
mPos.x= mpBitmapInfo->biWidth - mDispR.Width() ;
if( mPos.y > (mpBitmapInfo- >biHeight- mDispR.Height()))
mPos.y= mpBitmapInfo- >biHeight- mDispR.Height();
if( mPos.x < 0 ) mPos.x= 0;
if( mPos.y < 0 ) mPos.y= 0;
{
// 昄真彩位图
::BitBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),
hMemDC,mPos.x,mPos.y, SRCCOPY);
} else {
::StretchBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),
hMemDC,0,0, mpBitmapInfo- >biWidth, mpBitmapInfo-
>biHeight, SRCCOPY);
}
// l束昄真彩位图
::DeleteObject(SelectObject(hMemDC,hBitmapOld));
// ????br />} else {
mPos.x= mpBitmapInfo- >biWidth - mDispR.Width() ;
if( mPos.y > (mpBitmapInfo- >biHeight- mDispR.Height()))
mPos.y= mpBitmapInfo- >biHeight- mDispR.Height();
if( mPos.x < 0 ) mPos.x= 0;
if( mPos.y < 0 ) mPos.y= 0;
DrawDibRealize( m_hDrawDib, dc.GetSafeHdc(), TRUE);
{
Wid.x= mDispR.Width();
Wid.y= mDispR.Height();
// 1:1 昄? 不能大于囑Ş大小
if( Wid.x > mpBitmapInfo- >biWidth )
Wid.x = mpBitmapInfo- >biWidth;
if( Wid.y > mpBitmapInfo- >biHeight)
Wid.y = mpBitmapInfo- >biHeight;
, 0, 0, Wid.x, Wid.y,
mpBitmapInfo, (LPVOID) (mpBitsSrc+bfoffBits),
mPos.x, mPos.y, Wid.x, Wid.y, DDF_BACKGROUNDPAL);
} else {
DrawDibDraw( m_hDrawDib, dc.GetSafeHdc(),
0, 0, mDispR.Width(), mDispR.Height(),
mpBitmapInfo, (LPVOID)
]]>
]]>
CSDN - 文档中心
- 其他开发语a
阅读Q?span id="ArticleTitle1_ArticleTitle1_lblReadCount">42
评论Q?
0
参与评论
标题
一U位囄攄快速算?amp;PSD格式的开?amp;PSD格式文g的读?/span>
选择?byxdaz ?Blog
关键?/font>
一U位囄攄快速算?amp;PSD格式的开?amp;PSD格式文g的读?/span>
出处
l定一个位图,如何它~放至Q意尺寸?很明显,唯一的方法是Q放大时Q在像素中间d一些重复像
素,使图像拉宽;~小Ӟ把部分像素删除掉Q囑փ收羃。但是如何确定哪些像素该重复Q哪些像素该删除呢?下面的方法是我自已想的,如果你有更好的方法,
请告诉我。我们只考虑水^方向Q垂直方向跟水^方向是同L道理Q。下面先从简单的例子来说Q最后再推出一个通用的算法:
若要攑֤1倍,应将每一个像素都重复一ơ,N个像素变成了2N个像素,囑փ攑֤?倍。这个不难;
若要~小1/2Q应该每隔一个像素删除一个像素,2N个像素变成了N个像素,囑փ~小一半。这个也不难Q?br />
若要攑֤1.5倍,怎么办?假设原有2N个像素,现在Ʋ变?N个像素,L加N个像素,所以应对原图每隔一个像素添加一个重复像素:
若要~小1/3Q就是C的逆过E:每隔两个像素删除一个像素。?br />
上面四个例子都是比较Ҏ的特例。现在来考虑通用的算法。在四个例子的羃放过E可以这L解。假设欲长度ؓN1的像素列变成长度为N2的像素列Q首
先,讄两个指针Q一个作为源指针Q指向原来的像素列,d源像素,另一个作为目的指针,指向变换后的像素列,写入d到的像素。然后,以拉伸后像素列的
长度为@环次敎ͼ循环N2ơ,每次循环中由源指针处COPY一个像素到目的指针处,然后目的指针加一Q源指针Ҏ每次循环的不同需要增加一定步长(攑֤?
步长是零或一Q羃时步长大于{于一Q?br /> 法的框架解决了Q但是中心内容仍没有解决Q如何确定每ơ@环里源指针增加的步长Q或者说Q每ơ@?
里如何更新源指针的位|?Ҏ看出Q通过点q算很容易解册个问题:讄一个初gؓ零的点变量Q每ơ@环中Q把q个点变量加上N1/N2Qƈ其l?
果的整数部分作ؓ源指针距v始位|的偏移量;q样Q经qN2ơ@环,点变量的值恰好达到N1Q源指针也恰好“走”到原像素列的末?br /> q个
Ҏ可行Q但是太慢。如果能Q点运{化成整数q算快多了。那么如何{化呢Q我们可以设立一个值域为N1*N2的整数计数器Q每ơ@环递增N1Qƈ?
规定Q计数器每增加N2Q源指针前q一个像素。这Pl过N2ơ@环,计数器共增加了N1*N2Q源指针则增加了N1个单元,恰好“走”完全程。实际编
E中Q我们是用一个值域为N2的整数计数器Q超出值域部分取模处理。算法大致如下:
void StrechPixels(int N1, int N2, PIXEL src_pixels[], PIXEL dest_pixels[])
{
ASSERT(N1 <= N2); // N1 must <= N2
int p1 = 0, count = 0;
for (int p2 = 0; p2 < N2; p2++)
{
dest_pixels[p2] = src_pixels[p1];
count += N1;
while (count >= N2)
{
count -= N2;
p1++;
}
}
}
上面法只是水^~放单行像素Q对垂直方向也采用同L法Q便实现了Q意比例的位图~放。经q以上算法的处理Q放大时囑փ出现马赛克,~小时图像出?
闪砾。若要获得高质量的羃攑֛形,采用插倹{过滤等技术。但是因些技术所需计算量太大,在游戏中通常靠硬件加速来实现Q不宜Y件解冟뀂 ?br />
================================================================
PSD格式的开发?br />
我在做游戏时Q因用到PSD格式转换成BMP或者JPG格式的程序,而且Q在转换Ӟ要将PSD中的I格转成游戏中约定的透明Ԍq样的程序,?
能自己去写了。所以,我在|上搜了一阵子Q找C“中国游戏开发者”的|站Q看C一关于PSD格式的文章(q也是我开始向q个|站投稿的原因,也许q?
叫~)?br /> 本来是想L懒,省去了自qI之苦,可以抄一抄别人现在的代码Q再自己ҎQ又能省旉Q又能学C西,何乐而不为呢Q可是,?
抄下q篇文章的代码之后,发现其运行居然是不能通过的。看来天下没有免费的午餐Q我q是得自qI。一个多时的苦战之后,l于发现了问题所在,也许qƈ
不是一个问题,只是对于没有q个l验人来_q确实是个大问题。我现在这文章的一些地方进行改正,望各位朋友在开发PSD格式的读取问题上Q不再有?
烦。原文《PSD格式文g的读取》在q里http:
//cgd.pages.com.cn/cgd/develop/effect/200109/ReadPSD.htmQ各位可以看看。我只将我的Ҏ?
分写在下面:
1Q文件头?个字节,只能?个字节?br /> 2QPhotoshop的PSD格式用的是LIT格式存储?br />
q个LIT格式我以前只是听说过Q没惛_会被PSD用上。这个格式是数据的高低位码交换了的Q如果直接用ReadFile或者fread函数其d
来,它的高低位码是被交换了的。例如:640?6q制值是1E0Q用DWORD方式存在盘里是0001E000Q用L件的函数d来以后,变成:
00E00100。所以,光低位码被交换了,解决的方法是用{换函敎ͼ代码如下Q?br />
WORD WORDBIGtoLIT(WORD code) // 字型的处?br /> {
return ((a >> 8 & 0xFF) | ((a & 0x00FF) << 8); // 把高低位码再交换q来
}
DWORD DWORDBIGtoLIT(DWORD code) // 双字型的处理
{
WORD HiCode, LowCode;
HiCode = code & 0xFFFF0000;
LowCode = code & 0x0000FFFF;
HiCode = ((HiCode >> 8) & 0xFF) | ((HiCode & 0x00FF) << 8);
LowCode ((LowCode >> 8) & 0xFF) | ((LowCode & 0x00FF) << 8);
return MAKELONG((WORD)(LowCode << 16), (WORD)HiCode);
}
当然Q也可以定义成宏形式Q如下:
#define BIG2LIT(a) (WORD((a >> 8 & 0xFF) | ((a & 0x00FF) << 8)))
#define DWORDBIG2LIT(b) MAKELONG(BIG2LIT(HIWORD(b)), BIG2LIT(LOWORD(b)))
看v来简单一些,哈哈……。其它的Q原文没有什么错误,不过Q我大家q是最好自己去解决问题Q呵呵,因ؓ有一句话说得很好Q老师能教你读书写字,但是不能教你做天下文章。?br />
================================================================
PSD格式文g的读取?br />
PhotoShopQ我x有h会不知道吧。如今最新的版本?.0Q其图象文g*.PSD?.5相比变化q不太大。以下我׃l?.PSD文g的读
取方法,q提供完整读取函数。其中:m_Rect为目标区域,m_lpDDS7为目标DirectDraw表面Qm_pbAlphaMask为目?
Aplha通告指针。Read16函数Z指定文g当前位置d一个WORDQRead32函数Z指定文g当前位置d一个DWORD?
MAX_PSD_CHANNELS?4。以下就?.PSD文g的读取方法,有兴的朋友可以l箋深入研究Q到时可别忘了发我一份?br />
HRESULT LoadPSD( LPSTR strFilename ) // dPSD文g
{
DWORD dwWidth, dwHeight; // 宽高
long lSurfWidth = m_Rect.right - m_Rect.left;
long lSurfHeight = m_Rect.bottom - m_Rect.top;
WORD CompressionType; // 压羃cd
HDC hDC;
FILE *fpPSD;
WORD ChannelCount; // 通道?br />
// 打开PSD文g
if ( ( fpPSD = fopen ( strFilename, "rb" ) ) == NULL ) {
return E_FAIL;
}
// 头四个字节ؓ"8BPS"
char signature[5];
signature[0] = fgetc( fpPSD );
signature[1] = fgetc( fpPSD );
signature[2] = fgetc( fpPSD );
signature[3] = fgetc( fpPSD );
signature[4] = '\0';
if ( strcmp( signature,"8BPS" ) != 0 ) {
return E_FAIL;
}
// 版本必须?
if ( Read16( fpPSD ) != 1 ) {
return E_FAIL;
}
// 跌一些数据?L0)
Read32( fpPSD );
Read16( fpPSD );
// d通道?br /> ChannelCount = Read16( fpPSD );
// 定臛_有一个通道
if ( ( ChannelCount < 0 ) || ( ChannelCount > MAX_PSD_CHANNELS ) ) {
return E_FAIL;
}
// d宽和?br /> dwHeight = Read32( fpPSD );
dwWidth = Read32( fpPSD );
if ( dwWidth != ( DWORD )lSurfWidth || dwHeight != ( DWORD )lSurfHeight ) {
return E_FAIL;
}
// 只读?位通道
if ( Read16( fpPSD ) != 8 ) {
return E_FAIL;
}
// 定模式为RGB.
// 可能|
// 0: 位图
// 1: 灰阶
// 2: 索引
// 3: RGB
// 4: CMYK
// 7: Multichannel
// 8: Duotone
// 9: Lab
if ( Read16( fpPSD ) != 3 ) {
return E_FAIL;
}
// 跌数据Q如调色板)
int ModeDataCount = Read32( fpPSD );
if ( ModeDataCount )
fseek( fpPSD, ModeDataCount, SEEK_CUR );
// 跌数据Q如Qpen tool paths, etcQ?br /> int ResourceDataCount = Read32( fpPSD );
if ( ResourceDataCount )
fseek( fpPSD, ResourceDataCount, SEEK_CUR );
// 条过保留数据
int ReservedDataCount = Read32( fpPSD );
if ( ReservedDataCount )
fseek( fpPSD, ReservedDataCount, SEEK_CUR );
// 0: 非压~?br /> // 1: RLE压羃
CompressionType = Read16( fpPSD );
if ( CompressionType > 1 ) {
return E_FAIL;
}
BYTE* PSDPixels = new BYTE[ ( lSurfWidth * lSurfHeight ) * 4 ];
// 解包数据
UnPackPSD( fpPSD, lSurfWidth, lSurfHeight, PSDPixels, ChannelCount, CompressionType );
fclose( fpPSD );
// 复制信息
BITMAPINFO BitmapInfo;
ZeroMemory( &BitmapInfo, sizeof( BitmapInfo ) );
BitmapInfo.bmiHeader.biSize = sizeof( BitmapInfo.bmiHeader );
BitmapInfo.bmiHeader.biWidth = lSurfWidth;
BitmapInfo.bmiHeader.biHeight = -lSurfHeight;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
m_lpDDS7->GetDC( &hDC );
int rc = StretchDIBits( hDC,
0,
0,
lSurfWidth,
lSurfHeight,
0,
0,
lSurfWidth,
lSurfHeight,
PSDPixels,
&BitmapInfo,
DIB_RGB_COLORS,
SRCCOPY );
m_lpDDS7->ReleaseDC( hDC );
if ( rc == GDI_ERROR ) {
H_ARRAY_DELETE( PSDPixels );
#ifdef _DEBUG
g_pHERR->OutDebugMsg( 3, H2DSERR_INVALID_PSD );
#endif
return E_FAIL;
}
// 是否dAlpha混合通道
if( ChannelCount > 3 ) {
m_pbAlphaMask = new BYTE[ lSurfWidth * lSurfHeight ];
for ( int x = 0; x < lSurfWidth; x++ )
for ( int y = 0; y < lSurfHeight; y++ ) {
m_pbAlphaMask[ ( y * lSurfWidth ) + x ] =
PSDPixels[ ( ( ( y * lSurfHeight ) + x ) * 4 ) + 3 ];
}
}
else {
m_pbAlphaMask = NULL;
}
H_ARRAY_DELETE( PSDPixels );
return DD_OK;
}
// PSD文g解包
void CHades2DSurface::UnPackPSD( FILE *fp, // fp为PSD文g指针Q?br /> DWORD dwWidth, // dwWidth、dwHeight为宽高,
DWORD dwHeight,
BYTE* pixels, // pixels包目标指针,
WORD ChannelCnt, // ChannelCnt为通道敎ͼ
WORD Compression ) // Compression位压~类型。?br />
{
int Default[4] = { 0, 0, 0, 255 };
int chn[4] = { 2, 1, 0, 3};
int PixelCount = dwWidth * dwHeight;
if ( Compression ) {
fseek( fp, dwHeight * ChannelCnt * 2, SEEK_CUR );
for ( int c = 0; c < 4; c++ ) {
int pn = 0;
int channel = chn[c];
if ( channel >= ChannelCnt ) {
for ( pn=0; pn < PixelCount ;pn++ ) {
pixels[ ( pn * 4 ) + channel ] = Default[ channel ];
}
}
else // 非压~?br /> {
int count = 0;
while( count < PixelCount ) {
int len = fgetc( fp );
if( len == 128 ) { }
else if ( len < 128 ) // 非RLE
{
len++;
count += len;
while(len) {
pixels[ ( pn * 4 ) + channel ] = fgetc( fp );
pn++;
len--;
}
}
else if ( len > 128 ) // RLE打包
{
len ^= 0x0FF;
len += 2;
unsigned char val = fgetc( fp );
count += len;
while( len ) {
pixels[ ( pn * 4 ) + channel ] = val;
pn++;
len--;
}
}
}
}
}
}
else
{
for ( int c=0; c < 4; c++ ) {
int channel = chn[c];
if ( channel > ChannelCnt ) {
for( int pn = 0; pn < PixelCount; pn++ ) {
pixels[ ( pn * 4 ) + channel ] = Default[ channel ];
}
}
else {
for( int n = 0; n < PixelCount; n++ ) {
pixels[ ( n * 4 ) + channel ] = fgetc( fp );
}
}
}
}
}
]]>
首先Q在StdAfx.h中静态调用diplus.libQ即q译系l完成对DLL的加载,应用E序l束时卸载DLL的编码。如下:
#ifndef ULONG_PTR在类的头文g中定义,以下成员变量Q用来初始化GDI+的用和l束使用?
#define ULONG_PTR unsigned long*
#include "GdiPlus.h"
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")
#endif
GdiplusStartupInput m_gdiplusStartupInput;然后在OnCreate()函数中加入初始化GDI+的函敎ͼ
ULONG_PTR m_gdiplusToken;
GdiplusStartup(&m_gdiplusToken, &m_gdiplusStartupInput, NULL);在OnDestroy()函数中加入结束GDI+使用的函敎ͼ
GdiplusShutdown(m_gdiplusToken);接着Q定义{换函敎ͼ
BOOL MBmpToMImage(CMemFile& cbfBmp, CMemFile& cbfImage, CString strType)其中Q?
CMemFile& cbfBmp表示原位图文?该函C主要的处理ؓ以下几步Q?ol>
CMemFile& cbfImage表示转换后的囑Ş文g;
CString strType表示转换的图片类型?br />
详细代码如下Q?
BOOL MBmpToMImage(CMemFile& cbfBmp, CMemFile& cbfImage, CString strType)
{
int iBmpSize = cbfBmp.GetLength();
HGLOBAL hMemBmp = GlobalAlloc(GMEM_FIXED, iBmpSize);
if (hMemBmp == NULL) return FALSE;
IStream* pStmBmp = NULL;
CreateStreamOnHGlobal(hMemBmp, FALSE, &pStmBmp);
if (pStmBmp == NULL)
{
GlobalFree(hMemBmp);
return FALSE;
}
BYTE* pbyBmp = (BYTE *)GlobalLock(hMemBmp);
cbfBmp.SeekToBegin();
cbfBmp.Read(pbyBmp, iBmpSize);
Image* imImage = NULL;
imImage = Image::FromStream(pStmBmp, FALSE);
if (imImage == NULL)
{
GlobalUnlock(hMemBmp);
GlobalFree(hMemBmp);
return FALSE;
}
USES_CONVERSION;
CLSID clImageClsid;
GetImageCLSID(A2W("image/"+strType.GetBuffer(0)), &clImageClsid);
HGLOBAL hMemImage = GlobalAlloc(GMEM_MOVEABLE, 0);
if (hMemImage == NULL)
{
pStmBmp->Release();
GlobalUnlock(hMemBmp);
GlobalFree(hMemBmp);
if (imImage != NULL) delete imImage;
return FALSE;
}
IStream* pStmImage = NULL;
CreateStreamOnHGlobal(hMemImage, TRUE, &pStmImage);
if (pStmImage == NULL)
{
pStmBmp->Release();
GlobalUnlock(hMemBmp);
GlobalFree(hMemBmp);
GlobalFree(hMemImage);
if (imImage != NULL) delete imImage
return FALSE;
}
imImage->Save(pStmImage, &clJpgClsid);
if (pStmImage == NULL)
{
pStmBmp->Release();
pStmImage>Release();
GlobalUnlock(hMemBmp);
GlobalFree(hMemBmp);
GlobalFree(hMemImage;
if (imImage != NULL) delete imImage;
return FALSE;
}
LARGE_INTEGER liBegin = {0};
pStmImage->Seek(liBegin, STREAM_SEEK_SET, NULL);
BYTE* pbyImage = (BYTE *)GlobalLock(hMemImage);
cbfImage.SeekToBegin();
cbfImage.Write(pbyImage, GlobalSize(hMemImage));
if (imImage != NULL) delete imImage;
pStmBmp->Release();
pStmImage->Release();
GlobalUnlock(hMemBmp);
GlobalUnlock(hMemImage);
GlobalFree(hMemBmp);
GlobalFree(hMemImage);
return TRUE;
}
Some image files contain metadata that you can read to determine features of the image. For example, a digital photograph might contain metadata that you can read to determine the make and model of the camera used to capture the image. With Microsoft Windows GDI+, you can read existing metadata, and you can also write new metadata to image files.
GDI+ provides a uniform way of storing and retrieving metadata from image files in various formats. In GDI+, a piece of metadata is called a property item. You can store and retrieve metadata by calling the SetPropertyItem and GetPropertyItem methods of the Image class, and you don't have to be concerned about the details of how a particular file format stores that metadata.
GDI+ currently supports metadata for the TIFF, JPEG, Exif, and PNG file formats. The Exif format, which specifies how to store images captured by digital still cameras, is built on top of the TIFF and JPEG formats. Exif uses the TIFF format for uncompressed pixel data and the JPEG format for compressed pixel data.
GDI+ defines a set of property tags that identify property items. Certain tags are general-purpose; that is, they are supported by all of the file formats mentioned in the preceding paragraph. Other tags are special-purpose and apply only to certain formats. If you try to save a property item to a file that does not support that property item, GDI+ ignores the request. More specifically, the Image::SetPropertyItem method returns PropertyNotSupported.
You can determine the property items that are stored in an image file by calling Image::GetPropertyIdList. If you try to retrieve a property item that is not in the file, GDI+ ignores the request. More specifically, the Image::GetPropertyItem method returns PropertyNotFound.
The following console application calls the GetPropertySize method of an Image object to determine how many pieces of metadata are in the file FakePhoto.jpg.
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
INT main()
{
// Initialize <tla rid="tla_gdiplus"/>.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
UINT size = 0;
UINT count = 0;
Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
bitmap->GetPropertySize(&size, &count);
printf("There are %d pieces of metadata in the file.\n", count);
printf("The total size of the metadata is %d bytes.\n", size);
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;
}
The preceding code, along with a particular file, FakePhoto.jpg, produced the following output:
There are 7 pieces of metadata in the file.
The total size of the metadata is 436 bytes.
GDI+ stores an individual piece of metadata in a PropertyItem object. You can call the GetAllPropertyItems method of the Image class to retrieve all the metadata from a file. The GetAllPropertyItems method returns an array of PropertyItem objects. Before you call GetAllPropertyItems, you must allocate a buffer large enough to receive that array. You can call the GetPropertySize method of the Image class to get the size (in bytes) of the required buffer.
A PropertyItem object has the following four public members:
id | A tag that identifies the metadata item. The values that can be assigned to id (PropertyTagImageTitle, PropertyTagEquipMake, PropertyTagExifExposureTime, and the like) are defined in Gdiplusimaging.h. |
length | The length, in bytes, of the array of values pointed to by the value data member. Note that if the type data member is set to PropertyTagTypeASCII, then the length data member is the length of a null-terminated character string, including the NULL terminator. |
type | The data type of the values in the array pointed to by the value data member. Constants (PropertyTagTypeByte, PropertyTagTypeASCII, and the like) that represent various data types are described in Image Property Tag Type Constants. |
value | A pointer to an array of values. |
The following console application reads and displays the seven pieces of metadata in the file FakePhoto.jpg. The main function relies on the helper function PropertyTypeFromWORD, which is shown following the main function.
#include <windows.h>
#include <gdiplus.h>
#include <strsafe.h>
using namespace Gdiplus;
INT main()
{
// Initialize GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
UINT size = 0;
UINT count = 0;
#define MAX_PROPTYPE_SIZE 30
WCHAR strPropertyType[MAX_PROPTYPE_SIZE] = L"";
Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
bitmap->GetPropertySize(&size, &count);
printf("There are %d pieces of metadata in the file.\n\n", count);
// GetAllPropertyItems returns an array of PropertyItem objects.
// Allocate a buffer large enough to receive that array.
PropertyItem* pPropBuffer =(PropertyItem*)malloc(size);
// Get the array of PropertyItem objects.
bitmap->GetAllPropertyItems(size, count, pPropBuffer);
// For each PropertyItem in the array, display the id, type, and length.
for(UINT j = 0; j < count; ++j)
{
// Convert the property type from a WORD to a string.
PropertyTypeFromWORD(
pPropBuffer[j].type, strPropertyType, MAX_PROPTYPE_SIZE);
printf("Property Item %d\n", j);
printf(" id: 0x%x\n", pPropBuffer[j].id);
wprintf(L" type: %s\n", strPropertyType);
printf(" length: %d bytes\n\n", pPropBuffer[j].length);
}
free(pPropBuffer);
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;
} // main
// Helper function
HRESULT PropertyTypeFromWORD(WORD index, WCHAR* string, UINT maxChars)
{
HRESULT hr = E_FAIL;
WCHAR* propertyTypes[] = {
L"Nothing", // 0
L"PropertyTagTypeByte", // 1
L"PropertyTagTypeASCII", // 2
L"PropertyTagTypeShort", // 3
L"PropertyTagTypeLong", // 4
L"PropertyTagTypeRational", // 5
L"Nothing", // 6
L"PropertyTagTypeUndefined", // 7
L"Nothing", // 8
L"PropertyTagTypeSLONG", // 9
L"PropertyTagTypeSRational"}; // 10
hr = StringCchCopyW(string, maxChars, propertyTypes[index]);
return hr;
}
The preceding console application produces the following output:
Property Item 0
id: 0x320
type: PropertyTagTypeASCII
length: 16 bytes
Property Item 1
id: 0x10f
type: PropertyTagTypeASCII
length: 17 bytes
Property Item 2
id: 0x110
type: PropertyTagTypeASCII
length: 7 bytes
Property Item 3
id: 0x9003
type: PropertyTagTypeASCII
length: 20 bytes
Property Item 4
id: 0x829a
type: PropertyTagTypeRational
length: 8 bytes
Property Item 5
id: 0x5090
type: PropertyTagTypeShort
length: 128 bytes
Property Item 6
id: 0x5091
type: PropertyTagTypeShort
length: 128 bytes
The preceding output shows a hexadecimal ID number for each property item. You can look up those ID numbers in Image Property Tag Constants and find out that they represent the following property tags.
Hexadecimal value | Property tag |
---|---|
0x0320
0x010f 0x0110 0x9003 0x829a 0x5090 0x5091 |
PropertyTagImageTitle
PropertyTagEquipMake PropertyTagEquipModel PropertyTagExifDTOriginal PropertyTagExifExposureTime PropertyTagLuminanceTable PropertyTagChrominanceTable |
The second (index 1) property item in the list has id PropertyTagEquipMake and type PropertyTagTypeASCII. The following example, which is a continuation of the previous console application, displays the value of that property item:
printf("The equipment make is %s.\n", pPropBuffer[1].value);
The preceding line of code produces the following output:
The equipment make is Northwind Traders.
The fifth (index 4) property item in the list has id PropertyTagExifExposureTime and type PropertyTagTypeRational. That data type (PropertyTagTypeRational) is a pair of LONGs. The following example, which is a continuation of the previous console application, displays those two LONG values as a fraction. That fraction, 1/125, is the exposure time measured in seconds.
long* ptrLong = (long*)(pPropBuffer[4].value);
printf("The exposure time is %d/%d.\n", ptrLong[0], ptrLong[1]);
The preceding code produces the following output:
The exposure time is 1/125.
To write an item of metadata to an Image object, initialize a PropertyItem object and then pass the address of that PropertyItem object to the SetPropertyItem method of the Image object.
The following console application writes one item (the image title) of metadata to an Image object and then saves the image in the disk file FakePhoto2.jpg. The main function relies on the helper function GetEncoderClsid, which is shown in the topic Retrieving the Class Identifier for an Encoder.
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
INT main()
{
// Initialize <tla rid="tla_gdiplus"/>.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Status stat;
CLSID clsid;
char propertyValue[] = "Fake Photograph";
Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
PropertyItem* propertyItem = new PropertyItem;
// Get the CLSID of the JPEG encoder.
GetEncoderClsid(L"image/jpeg", &clsid);
propertyItem->id = PropertyTagImageTitle;
propertyItem->length = 16; // string length including NULL terminator
propertyItem->type = PropertyTagTypeASCII;
propertyItem->value = propertyValue;
bitmap->SetPropertyItem(propertyItem);
stat = bitmap->Save(L"FakePhoto2.jpg", &clsid, NULL);
if(stat == Ok)
printf("FakePhoto2.jpg saved successfully.\n");
delete propertyItem;
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;
}
//Cavicd文g定义Q?br />class CAvi file://AVIc,处理AVI文g { public: int cy;//图象?br /> int cx;//图象?br /> file://long m_maxFrame; BYTE *pData;//寸储图象数据 BITMAPINFO *m_pBMI;//位图文g信息?br /> PAVISTREAM pavi;//AVI?br /> PAVIFILE pfile;//AVI文g指针 AVIFILEINFO * pfi; file://AVI信息 BOOL AviRead(int mFrame);//读AVI文g的第mFrame?br /> CAvi();//标准构造函?br /> CAviCreate(CString &string);//用文件名初始化AVIcȝ成员 virtual ~CAvi(); }; //CavicL件实现部分; CAvi::CAvi() { AVIFileInit();//初始化AVI?br /> cx=0;//定义图象宽、高、等成员 cy=0; m_pBMI=NULL; pData=NULL; file://m_maxFrame=0; pfi=NULL; } CAvi::~CAvi()//析构、释放指?br />{ // AVIFileClose(pfile); AVIFileExit(); if(pData!=NULL) delete pData; pData=NULL; if(m_pBMI!=NULL) delete m_pBMI; m_pBMI=NULL; if(pfi!=NULL) delete pfi; pfi=NULL; } CAvi::CAviCreate(CString &string)//L件初始化该类 { HRESULT hr; pfi=new AVIFILEINFO; hr = AVIFileOpen(&pfile, // returned file pointer string, // file name OF_READ, // mode to open file with NULL); hr= AVIFileInfo(pfile, file://获取AVI信息Q放入pfi?br /> pfi, sizeof(AVIFILEINFO) ); cx=pfi->dwWidth;//图象宽、高 cy=pfi->dwHeight; hr=AVIFileGetStream(//AVI变成视频?br />pfile, &pavi, streamtypeVIDEO, 0//LONG lParam ); m_pBMI=new BITMAPINFO;//定义BMP信息?br />m_pBMI->bmiHeader.biBitCount=24; m_pBMI->bmiHeader.biClrImportant=0; m_pBMI->bmiHeader.biClrUsed=0; m_pBMI->bmiHeader.biCompression=BI_RGB; m_pBMI->bmiHeader.biHeight=cy; m_pBMI->bmiHeader.biWidth=cx; m_pBMI->bmiHeader.biPlanes=1; m_pBMI->bmiHeader.biSize=sizeof(BITMAPINFOHEADER); m_pBMI->bmiHeader.biXPelsPerMeter=0; m_pBMI->bmiHeader.biYPelsPerMeter=0; m_pBMI->bmiHeader.biSizeImage=cx*cy*3; pData=(BYTE*)new char[cx*cy*3];//ҎAVI中BMP图象的信息定义缓冲区 } BOOL CAvi::AviRead(int mFrame)//AVI文g的M帧数据读入PData~冲?br />{ HRESULT hr; hr= AVIStreamRead( pavi, mFrame, 1, pData, cx*cy*3, NULL, NULL ); if(hr==0) return TRUE; else return FALSE; } |
1. 基本概念
先来用通俗的语句讲解位囑֒调色板的概念?br />?
们知道,自然界中的所有颜色都可以q、绿、蓝(RQGQB)三基色组合而成。针对含有红、绿、蓝色成分的多少Q可以对其分别分??55个等U,?
U、绿、蓝的不同组合共?56×256×256U,因此U能表示1600万种颜色。对于hD言Q这已经?真彩?了?br />
Ҏ个像素进行了QRQGQBQ量化的囑փ是位图Q其在计机中对应文件的扩展名一般ؓ.bmp。既然用RQGQB的量化值就可以直接记录一张位囄所有像素,那我们需要调色板q什么呢Q?br />
首先Q我们可以计完全利用(RQGQBQ组合来存储一?00×600的位图所需要的I间为:
800×600×3 = 1440000Q字节)Q?1.37MQ字节)
惊h的大Q因此,调色板横I出世了Q它的功能在于缓解位图文件存储空间过大的问题?br />
假设一个位图ؓ16Ԍ其像素L?00×600。我们只需要用4个bit可以存储这个位囄每个像素?6U颜色中所处的{Q然后调色板提供了这16U等U对应的QRQGQBQ|q样Q存储这?6色位囑֏需要:
800×600×4/8 = 240000Q字节)= 0.22 MQ字节)
额外的存储RQGQB表的开销Q即调色板PaletteQ也UCؓ颜色查找表LUTQ仅仅ؓ16×3Q?8字节?br />
存储I间被大为减!
常见的位图有单色?6艌Ӏ?56艌Ӏ?6位及24位真彩色5U,对于前三者(即不大于256Ԍ都可以调色板方式q行存储Q而对16位及24位真彩色以调色板q行存储是不划算的,它们直接按照RQGQB分量q行存储?br />
在此基础上我们来分析DDB位图QDevice-dependent bitmapQ与讑֤相关的位图)与DIB位图QDevice-independent bitmapQ与讑֤无关的位图)的概念以及二者的区别?br />
DDB依赖于具体设备,它只能存在于内存中(视频内存或系l内存)Q其颜色模式必须与特定的输出讑֤怸_使用pȝ调色ѝ一般只能蝲入色彩较单的DDB位图Q对于颜色较丰富的位图,需使用DIB才能长期保存?br />
DIB
不依赖于具体讑֤Q可以用来永久性地保存图象。DIB一般是?.BMP文g的Ş式保存在盘中的Q有时也会保存在*.DIB文g中?
DIB位图的特Ҏ颜色信息储存在位图文g自n的颜色表中,应用E序要根据此颜色表ؓDIB创徏逻辑调色ѝ因此,在输ZqDIB位图之前Q程序应?
其逻辑调色杉K入到相关的讑֤上下文ƈ实现到系l调色板中?br />
2. 例程q?br />
本文后箋的讲解都Zq样的一个例子工E,它是一个基于对话框的MFC应用E序Q包?个父菜单Q?br />
Q?Q?DDB位图
DDB位图父菜单又包括两个子菜单:
a. IDQIDM_LOADDDBPIC captionQ加?br />
单击事gQ加载资源中的DDB位图q显CZ
b. IDQIDM_MARK_DDBPIC captionQ标?br />
单击事gQ在DIB位图中透明地添加天极网logo
Q?Q?DIB位图
DIB位图父菜单又包括两个子菜单:
a. IDQIDM_OPENDIBPIC captionQ打开
单击事gQ弹出文件对话框Q打开.bmp位图文gQƈ昄
b. IDQIDM_MARK_DIBPIC captionQ标?br />
单击事gQ在DIB位图中透明地添加天极网logo
工程中还包含下列位图资源Q?br />
Q?QIDB_LOADED_BITMAPQ要加蝲的位图资?br />
Q?QIDB_YESKY_BITMAPQ天极网logo
后箋章集中在?个子菜单单击事g消息处理函数的讲解,下面的代码是整个对话框类CBitMapExampleDlg的消息映:BEGIN_MESSAGE_MAP(CBitMapExampleDlg, CDialog)
//{{AFX_MSG_MAP(CBitMapExampleDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_COMMAND(IDM_LOADDDBPIC, OnLoadddbpic)
ON_COMMAND(IDM_MARK_DDBPIC, OnMarkDdbpic)
ON_COMMAND(IDM_OPENDIBPIC, OnOpendibpic)
ON_COMMAND(IDM_MARK_DIBPIC,OnMarkDibpic) //}}AFX_MSG_MAP
END_MESSAGE_MAP()
3. DDB位图~程
先看DDB加蝲按钮的单M件代码:void CBitMapExampleDlg::OnLoadddbpic()
{
1: CBitmap bmpDraw;
2: bmpDraw.LoadBitmap( IDB_LOADED_BITMAP );//装入要加载的DDB位图
3: BITMAP bmpInfo;
4: bmpDraw.GetBitmap( &bmpInfo ); //获取要加载DDB位图的尺?
5: CDC memDC;//定义一个兼容DC
6: CClientDC dc( this );
7: memDC.CreateCompatibleDC( &dc );//创徏兼容DC
8: CBitmap* pbmpOld = memDC.SelectObject( &bmpDraw );//保存原有DDBQƈ选入新DDB入DC
9: dc.BitBlt( 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &memDC, 0, 0, SRCCOPY );
10: memDC.SelectObject( pbmpOld );//选入原DDB
}
上述代码生如?所C的效果Q位图被安置在对话框Q?,0Q坐标开始的位置上?/p>
? 加蝲DDB位图资源
我们来逐行解析上述代码是怎样产生?的效果的?br />
W??行定义了一个CBitmap对象Qƈ调用其成员函?
LoadBitmap加蝲工程中的位图资源IDB_LOADED_BITMAP。第3?行定义了BITMAPl构体的实例q调用CBitmap的成员函
数GetBitmap获得位图信息QBITMAPl构体定义在/* Bitmap Header Definition */
typedef struct tagBITMAP
{
LONG bmType; //必需?
LONG bmWidth; //位图的宽?以像素ؓ单位)
LONG bmHeight; //位图的高?以像素ؓ单位)
LONG bmWidthBytes; //每一扫描行所需的字节数Q应是偶?br />WORD bmPlanes; //色^面数
WORD bmBitsPixel; //色^面的颜色位数
LPVOID bmBits; //指向存储像素阵列的数l?br />} BITMAP, *PBITMAP, NEAR *NPBITMAP, FAR *LPBITMAP;
W?~8行的作用是:构徏一个CDC对象Q调用CDC::CreateCompatibleDC创徏一个兼容的内存讑֤上下文,接着调用CDC::SelectObjectDDB选入内存讑֤上下文中?br />
W?行调用函数CDC::BitBltl制位图QCDC::BitBlt的原型ؓQ?br />CDC::BitBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC, int xSrc, int ySrc, DWORD dwRop)
CDC::BitBlt执行的操作ؓ源DC中位囑֤制到目的DC中。其中前四个参数为目的区域的坐标Qx,yQ及长度和宽度(Width, nHeightQ,W五个参数是源DC指针Q接下来的参数是源DC中的起始坐标Q最后一个参Cؓ光栅操作的类型?br />
W?0行调用CDC::SelectObject把原来的DDB选入到内存设备上下文中ƈ使新DDBq出来?br />
与CDC::BitBlt对应的还有另一个函数CDC::StretchBltQ它h~放功能Q其原型为:BOOL CDC::StretchBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC, int
xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop);
该函数把位图从源矩Ş拯到目的矩形中Q如果源和目的矩形尺怸同,那么羃放位囄功能以适应目的矩Ş的大。函数的大部分参CBitBlt的相同,但多了两个参数nSrcWidth和nSrcHeight用来指定源矩形的宽和高?br />
如果我们函数CBitMapExampleDlg::OnLoadddbpic() 中的W?行改为:CRect clientRect;
GetClientRect(&clientRect); //获得对话框窗口的大小
dc.StretchBlt(0, 0, clientRect.right, clientRect.bottom, &memDC, 0, 0,
bmpInfo.bmWidth, bmpInfo.bmHeight, SRCCOPY);
则单d载按钮后的对话框如图2所C,位图被拉伸至整个对话框的范围?br />
? 拉位图
CDC::BitBlt和dc.StretchBlt函数中的dwRop参数较ؓ有用Q它定义光栅操作的类型。请?DDB位图"父菜单下"标记"子菜单单M件的消息处理函数代码Q?br />void CBitMapExampleDlg::OnMarkDdbpic()
{
CBitmap bmpDraw;
bmpDraw.LoadBitmap(IDB_YESKY_BITMAP); //装入天极|logo DDB位图资源
BITMAP bmpInfo;
bmpDraw.GetBitmap(&bmpInfo); //获取天极|logo位图的尺?
CDC memDC; //定义一个兼容DC
CClientDC dc(this);
memDC.CreateCompatibleDC(&dc); //创徏DC
CBitmap *pbmpOld = memDC.SelectObject(&bmpDraw);
//保存原有DDBQƈ选入天极|logo位图入DC
dc.BitBlt(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &memDC, 0, 0, SRCAND);
memDC.SelectObject(pbmpOld); //选入原DDB
}
单击该按钮后Q将产生如图3的效果,天极|的logo被透明地添加到了位图中Q?br />
? 在DDB位图中加入天极网logo
能生这个效果的原因在于我们在代码行Q?br />dc.BitBlt ( 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &memDC, 0, 0, SRCAND );
中用了参数SRCANDQ不同于先前代码中SRCCOPYQ它仅仅意味着复制源位囑ֈ目的位图Q,它的含义为源和目的间q行AND?
作。我们不知道天极|的~辑同志是怎么为文章中的图片加logo的,有可能他们就使用了具有自动AND功能的图像加logo批处理Y件。的,我们可以?
用例E中的原理写一个批处理软gQ一ơ对一堆图片自动添加logo?br />
参数dwRop除了可以为SRCAND和SRCCOPY外,q可以有如下取|
BLACKNESSQ输出区域ؓ黑色
DSTINVERTQ反转目的位?
MERGECOPYQ用与操作把图案(Pattern)与源位图融合h
MERGEPAINTQ用或操作把反{的源位图与目的位图融合v?
NOTSRCCOPYQ把源位囑֏转然后拷贝到目的?
NOTSRCERASEQ用或操作融合源和目的位图,然后再反?
PATCOPYQ把图案拯到目的位图中
PATINVERTQ用异或操作把图案与目的位图相融?
PATPAINTQ用或操作融合图案和反{的源位图Q然后用或操作把l果与目的位图融?
SRCERASEQ先反{目的位图Q再用与操作其与源位图融合
SRCINVERTQ用异或操作融合源位囑֒目的位图
SRCPAINTQ用或操作融合源位图和目的位?
WHITENESSQ输出区域ؓ白色
合理利用q些取值将帮助我们制作出特定要求的囑փ处理软g?br />
从上q实例我们可以看出,在VC中用CBitmapc,必须位图放入工E的资源中,q用类 CBitmap的成员函数LoadBitmap加蝲之,再通过CDCcȝ成员函数BitBltq行DC拯{操作达到显C的目的。CBitmap有显C的不Q?br />
Q?Q?位图需要放入工E资源中Q这导致工E的可执行文件变大;
Q?Q?因ؓ位图需攑օ工程资源中,而资源中不能无穷无尽地包含位图,应用E序无法自适应地选取其它位图Q能使用的位囑֍分有限的Q?br />
Q?Q?cCBitmap只是DDB位图操作API的封装,不能独立于^台?br />
DIB位图则可以解决上q问题,其特Ҏ?BMP位图文g格式存储独立于^台的囑փ数据Q下面我们来详细分析?br />4. DIB位图~程
4.1位图文g格式
先来分析DIB位图文g的格式。位图文件分为四部分Q?
Q?Q位图文件头BITMAPFILEHEADER
位图文g头BITMAPFILEHEADER是一个结构体Q长度ؓ14字节Q定义ؓQ?br />typedef struct tagBITMAPFILEHEADER
{
WORD bfType; //文gcdQ必L0x424DQ即字符?BM"
DWORD bfSize; //文g大小Q包括BITMAPFILEHEADER?4个字?br />WORD bfReserved1; //保留?br />WORD bfReserved2; //保留?br />DWORD bfOffBits; //从文件头到实际的位图数据的偏Ud节数
} BITMAPFILEHEADER;
Q?Q位图信息头BITMAPINFOHEADER
位图信息头BITMAPINFOHEADER也是一个结构体Q长度ؓ40字节Q定义ؓQ?br />typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; //本结构的长度Qؓ40
LONG biWidth; //图象的宽度,单位是象?br />LONG biHeight; //图象的高度,单位是象?br />WORD biPlanes; //必须?
WORD biBitCount;
//表示颜色时要用到的位敎ͼ1(单色), 4(16?, 8(256?, 24(真彩?
DWORD biCompression;
//指定位图是否压羃Q有效的gؓBI_RGBQBI_RLE8QBI_RLE4QBI_BITFIELDS{,BI_RGB表示不压~?br />DWORD biSizeImage;
//实际的位图数据占用的字节敎ͼ?biSizeImage=biWidth?× biHeightQbiWidth’是biWidth 按照4的整倍数调整后的l果
LONG biXPelsPerMeter; //目标讑֤的水q_辨率Q单位是每米的象素个?br />LONG biYPelsPerMeter; //目标讑֤的垂直分辨率Q单位是每米的象素个?br />DWORD biClrUsed; //位图实际用到的颜色数Q?表示颜色Cؓ2biBitCount
DWORD biClrImportant; //位图中重要的颜色敎ͼ0表示所有颜色都重要
} BITMAPINFOHEADER;
Q?Q调色板Palette
?
色板Palette针对的是需要调色板的位图,卛_艌Ӏ?6色和256色位图。对于不以调色板方式存储的位图,则无此项信息。调色板是一个数l,共有
biClrUsed个元?如果该gؓ0Q则?biBitCount个元?。数l中每个元素是一个RGBQUADl构体,长度?个字节,定义为:typedef struct tagRGBQUAD
{
BYTE rgbBlue; //蓝色分量
BYTE rgbGreen; //l色分量
BYTE rgbRed; //U色分量
BYTE rgbReserved; //保留?br />} RGBQUAD;
Q?Q实际的位图数据ImageDate
对于用到调色板的位图Q实际的图象数据ImageDate象素的颜色在调色板中的烦引|对于真彩色图Q图象数据则为实际的R、G、B|
a.单色位图Q用1bit可以表C素的颜色索引|
b.16色位图:?bit可以表示象素的颜色烦引|
c. 256色位图:1个字节表C?个象素的颜色索引|
d.真彩Ԍ3个字节表C?个象素的颜色RQGQB倹{?br />
此外Q位图数据每一行的字节数必Mؓ4的整倍数Q如果不是,则需要补齐。奇怪的是,位图文g中的数据是从下到上(而不是从上到下)、从左到x式存储的?br />4.2位图的显C?br />
Visual C++ MFC中没有提供一个专门的cL处理DIB位图Q因此,Z方便C用位图文Ӟ我们有必要派生一个CDibcR类的源代码如下Q?br />
(1) CDibcȝ声明// DIB.hQ类CDib声明头文?br />#ifndef __DIB_H__
#define __DIB_H__
#include
class CDib
{
public:
CDib();
~CDib();
BOOL Load( const char * );
BOOL Save( const char * );
BOOL Draw( CDC *, int nX = 0, int nY = 0, int nWidth = -1, int nHeight = -1, int mode = SRCCOPY);
BOOL SetPalette( CDC * );
private:
CPalette m_Palette;
unsigned char *m_pDib, *m_pDibBits;
DWORD m_dwDibSize;
BITMAPINFOHEADER *m_pBIH;
RGBQUAD *m_pPalette;
int m_nPaletteEntries;
};
#endif
(2) CDibcȝ实现// DIB.cppQ类CDib实现文g
#include "stdafx.h"
#include "DIB.h"
CDib::CDib()
{
m_pDib = NULL;
}
CDib::~CDib()
{
// 如果位图已经被加载,释放内存
if (m_pDib != NULL)
delete []m_pDib;
}
下面q个函数非常重要Q其功能为加载位图,cM于CBitmapcȝLoadBitmap函数Q?br />BOOL CDib::Load(const char *pszFilename)
{
CFile cf;
// 打开位图文g
if (!cf.Open(pszFilename, CFile::modeRead))
return (FALSE);
// 获得位图文g大小Qƈ减去BITMAPFILEHEADER的长?br />DWORD dwDibSize;
dwDibSize = cf.GetLength() - sizeof(BITMAPFILEHEADER);
// 为DIB位图分配内存
unsigned char *pDib;
pDib = new unsigned char[dwDibSize];
if (pDib == NULL)
return (FALSE);
BITMAPFILEHEADER BFH;
// d位图文g数据
try
{
// 文g格式是否正确有效
if ( cf.Read(&BFH, sizeof(BITMAPFILEHEADER)) != sizeof(BITMAPFILEHEADER) ||
BFH.bfType != ’MB?|| cf.Read(pDib, dwDibSize) != dwDibSize)
{
delete []pDib;
return (FALSE);
}
}
catch (CFileException *e)
{
e->Delete();
delete []pDib;
return (FALSE);
}
// delete先前加蝲的位?br />if (m_pDib != NULL)
delete m_pDib;
// 时Dib数据指针和Dib大小变量赋给cL员变?br />m_pDib = pDib;
m_dwDibSize = dwDibSize;
// 为相应类成员变量赋BITMAPINFOHEADER和调色板指针
m_pBIH = (BITMAPINFOHEADER*)m_pDib;
m_pPalette = (RGBQUAD*) &m_pDib[sizeof(BITMAPINFOHEADER)];
// 计算调色板中实际颜色数量
m_nPaletteEntries = 1 << m_pBIH->biBitCount;
if (m_pBIH->biBitCount >8)
m_nPaletteEntries = 0;
else if (m_pBIH->biClrUsed != 0)
m_nPaletteEntries = m_pBIH->biClrUsed;
// 为相应类成员变量赋image data指针
m_pDibBits = &m_pDib[sizeof(BITMAPINFOHEADER) + m_nPaletteEntries * sizeof (RGBQUAD)];
// delete先前的调色板
if (m_Palette.GetSafeHandle() != NULL)
m_Palette.DeleteObject();
// 如果位图中存在调色板Q创建LOGPALETTE 及CPalette
if (m_nPaletteEntries != 0)
{
LOGPALETTE *pLogPal = (LOGPALETTE*)new char[sizeof(LOGPALETTE) + m_nPaletteEntries *sizeof(PALETTEENTRY)];
if (pLogPal != NULL)
{
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = m_nPaletteEntries;
for (int i = 0; i < m_nPaletteEntries; i++)
{
pLogPal->palPalEntry[i].peRed = m_pPalette[i].rgbRed;
pLogPal->palPalEntry[i].peGreen = m_pPalette[i].rgbGreen;
pLogPal->palPalEntry[i].peBlue = m_pPalette[i].rgbBlue;
}
//创徏CPaletteq放LOGPALETTE的内?br />m_Palette.CreatePalette(pLogPal);
delete []pLogPal;
}
}
return (TRUE);
}
//函数功能Q保存位囑օBMP文g
BOOL CDib::Save(const char *pszFilename)
{
if (m_pDib == NULL)
return (FALSE);
CFile cf;
if (!cf.Open(pszFilename, CFile::modeCreate | CFile::modeWrite))
return (FALSE);
try
{
BITMAPFILEHEADER BFH;
memset(&BFH, 0, sizeof(BITMAPFILEHEADER));
BFH.bfType = ’MB?
BFH.bfSize = sizeof(BITMAPFILEHEADER) + m_dwDibSize;
BFH.bfOffBits = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) + m_nPaletteEntries *sizeof(RGBQUAD);
cf.Write(&BFH, sizeof(BITMAPFILEHEADER));
cf.Write(m_pDib, m_dwDibSize);
}
catch (CFileException *e)
{
e->Delete();
return (FALSE);
}
return (TRUE);
}
下面q个函数也非帔R要,其功能ؓ在pDC指向的CDC中绘制位图,L坐标?nX,nY)Q绘制宽度和高度为nWidth、nHeightQ最后一个参数是光栅模式Q?br />BOOL CDib::Draw(CDC *pDC, int nX, int nY, int nWidth, int nHeight, int mode)
{
if (m_pDib == NULL)
return (FALSE);
// 获取位图宽度和高度赋?br />if (nWidth == - 1)
nWidth = m_pBIH->biWidth;
if (nHeight == - 1)
nHeight = m_pBIH->biHeight;
// l制位图
StretchDIBits(pDC->m_hDC,
nX, nY, nWidth, nHeight, 0, 0, m_pBIH->biWidth, m_pBIH->biHeight,
m_pDibBits, (BITMAPINFO*)m_pBIH, BI_RGB, mode);
return (TRUE);
}
//函数功能Q设|调色板
BOOL CDib::SetPalette(CDC *pDC)
{
if (m_pDib == NULL)
return (FALSE);
// 查当前是否有一个调色板句柄Q对于大?56色的位图QؓNULL
if (m_Palette.GetSafeHandle() == NULL)
return (TRUE);
// 选择调色板,接着实施之,最后恢复老的调色?br />CPalette *pOldPalette;
pOldPalette = pDC->SelectPalette(&m_Palette, FALSE);
pDC->RealizePalette();
pDC->SelectPalette(pOldPalette, FALSE);
return (TRUE);
}
从整个CDibcȝ代码中我们可以看出,DIB位图的显C需遵@如下步骤Q?br />
Q?Q读取位图,本类中用pDib = new unsigned char[dwDibSize]Z图中的信息分配内存,另一U方法是调用API函数CreateDIBSectionQ譬如:m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(),
(LPBITMAPINFO) m_lpBMPHdr, DIB_RGB_COLORS,
(LPVOID*) &m_lpDIBits, NULL, 0);
m_hBitmap定义为:HBITMAP m_hBitmap;
Q?Q根据读取的位图信息Q计出调色板大,然后创徏调色板;
Q?Q调用CDib::SetPalette( CDC *pDC )讄调色板,需要用到CDC::SelectPalette及CDC::RealizePalette两个函数Q?br />
Q?Q?
调用CDib::Draw(CDC *pDC, int nX, int nY, int nWidth, int nHeight, int
mode)函数l制位图。在此函CQ真正发挥显CZ图作用的是对StretchDIBits
API函数的调用。StretchDIBits函数h~放功能Q其最后一个参C是光栅操作的模式?br />
下面l出DIB位图的打开及显Cƈ在其中加入天极网logo的函数源代码?DIB位图"父菜单下"打开"子菜单的单击事g消息处理函数为(其功能ؓ打开位图q显CZQ: void CBitMapExampleDlg::OnOpendibpic()
{
// 弹出文g对话框,让用户选择位图文g
CFileDialog fileDialog(TRUE, "*.BMP", NULL, NULL,"位图文g(*.BMP)|*.bmp;*.BMP|");
if (IDOK == fileDialog.DoModal())
{
// 加蝲位图q显CZ
CDib dib;
if (dib.Load(fileDialog.GetPathName()))
{
CClientDC dc(this);
dib.SetPalette(&dc);
dib.Draw(&dc);
}
}
}
"DIB位图"父菜单下"标记"子菜单的单击事g消息处理函数为(其功能ؓl位囑֊上天极网logoQ:void CBitMapExampleDlg::OnMarkDibpic()
{
// 弹出文g对话框,让用户选择标记logo
CFileDialog fileDialog(TRUE, "*.BMP", NULL, NULL, "标记位图文g(*.BMP)|*.bmp;*.BMP|");
if (IDOK == fileDialog.DoModal())
{
// 加蝲标记logo位图q与目标位图怸
CDib dib;
if (dib.Load(fileDialog.GetPathName()))
{
CClientDC dc(this);
dib.SetPalette(&dc);
dib.Draw(&dc, 0, 0, - 1, - 1, SRCAND);
}
}
}
?昄了DIB位图加蝲天极|logo后的效果Q要好于?中加天极|logo后的DDB位图。图4昄的是真彩色位囄互与的结果,而图3中的囑փ颜色被减了?br />
? 在DIB位图中加入天极网logo
5. l束?br />
本文介绍了位囑֏调色板的概念Qƈ讲解了DDB位图与DIB位图的区别。在此基
上,本文以实例讲解了DDB位图和DIB位图的操作方式。DDB位图的处理相Ҏ较简单,对于DIB位图Q我们需要定义一个MFC所没有的新cCDibQ?
它屏蔽位图信息的d及调色板创徏的技术细节,应用E序可以方便C用之?br />
本文中的所有程序在Visual C++6.0及Windows XPq_上调试通过?/p>