西安交通大学流体机械研究所
张义云
7 E# p2 J* j! p- @2 f3 [: u: H- r
---- 用普通方法显示BMP位图,占内存大,速度慢,在图形缩小时,失真严重,
在低颜色位数的设备上显示高颜色位数的图形图形时失真大。本文采用视频函数
+ K" l2 y8 R! w0 T# r3 x& i显示BMP位图,可以消除以上的缺点。
---- 一、BMP文件结构
' { \" V/ |) M9 c! t5 i7 H: v/ o' M8 I9 T/ x% o
---- 1. BMP文件组成
---- BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。
9 j( u! X$ {5 m/ W+ ]3 H+ @9 v. \& L5 }6 f `0 ^1 a" J* ^
---- 2. BMP文件头
6 }5 e. F' c. n# ~! k0 s+ @9 ^1 U* w6 m0 J( `8 u: J
---- BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息
。
$ i3 y" Y* ?# Z' f: _' M
---- 其结构定义如下:
$ _- E* K- F" Z) E0 ?5 H& g0 n) J! `7 n- f
typedef struct tagBITMAPFILEHEADER
{
WORDbfType; // 位图文件的类型,必须为BM
DWORD bfSize; // 位图文件的大小,以字节为单位
WORDbfReserved1; // 位图文件保留字,必须为0
WORDbfReserved2; // 位图文件保留字,必须为0
& ]( f+ r4 O" Q- _( SDWORD bfOffBits; // 位图数据的起始位置,以相对于位图
// 文件头的偏移量表示,以字节为单位
1 f5 g- F: v7 l: ?& |" M5 i/ s} BITMAPFILEHEADER;
$ t/ c h: X6 S! T/ e3 d
---- 3. 位图信息头
----
k; i( s: p) E8 W4 Q
BMP位图信息头数据用于说明位图的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{
6 Q4 Y- i a7 w( h+ XDWORD biSize; // 本结构所占用字节数
3 }+ W+ l" p6 N1 C7 M3 ~% XLONGbiWidth; // 位图的宽度,以像素为单位
( \! J3 r8 [# u* FLONGbiHeight; // 位图的高度,以像素为单位
WORD biPlanes; // 目标设备的级别,必须为1
T* W# `7 f: ~: ~" C# C3 \WORD biBitCount// 每个像素所需的位数,必须是1(双色),
// 4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),
0 @! f1 g2 `7 ]5 g) q// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
3 C. |, X; {* ~$ h1 _7 \( O% JDWORD biSizeImage; // 位图的大小,以字节为单位
6 R9 D4 Y: B. K, b7 A5 sLONGbiXPelsPerMeter; // 位图水平分辨率,每米像素数
LONGbiYPelsPerMeter; // 位图垂直分辨率,每米像素数
DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数
DWORD biClrImportant;// 位图显示过程中重要的颜色数
, N+ c" G2 N2 n) J# g5 S) n} BITMAPINFOHEADER;
% c, v' I+ ^9 f+ ]
4 f+ L0 f; F, N- V0 h" M# Q; P# y
---- 4. 颜色表
---- 颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个
RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
* N- L7 n( A' \" o$ }6 D$ U0 i% l7 |; `
typedef struct tagRGBQUAD {
BYTErgbBlue;// 蓝色的亮度(值范围为0-255)
8 f3 I4 Y) Q. F: tBYTErgbGreen; // 绿色的亮度(值范围为0-255)
BYTErgbRed; // 红色的亮度(值范围为0-255)
BYTErgbReserved;// 保留,必须为0
} RGBQUAD;
颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
: [# @' `( X1 I当biBitCount=1,4,8时,分别有2,16,256个表项;
当biBitCount=24时,没有颜色表项。
1 i" \; ]6 Z- T) u3 T位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
typedef struct tagBITMAPINFO {
" {9 c4 X& ^& f: ?/ k! c4 M( |1 NBITMAPINFOHEADER bmiHeader; // 位图信息头
0 F3 y/ ~3 B! z) Y% wRGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO;
6 C2 f9 r2 l2 Q: i+ R2 m
$ i- s" d$ b6 V5 \" H' c( K: ?
1 b' l# }( \" h0 J* \
---- 5. 位图数据
---- 位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右
,扫描行之间是从下到上。位图的一个像素值所占的字节数:
5 i& e* c; C. S. n7 W6 A3 {. Y/ [+ o6 C8 k' n% S
当biBitCount=1时,8个像素占1个字节;
?# ]& N! K% q; O当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
Windows规定一个扫描行所占的字节数必须是
# N7 P$ @' ~ E) T$ Y4的倍数(即以long为单位),不足的以0填充,
一个扫描行所占的字节数计算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;
; C1 m. R) l8 y8 y7 e1 @" B' _// 一个扫描行所占的字节数
/ A/ {1 P1 V, G. W0 |) w; EDataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数
位图数据的大小(不压缩情况下):
8 @4 O1 a9 \" xDataSize= DataSizePerLine* biHeight;
---- 二、BMP位图一般显示方法
---- 1. 申请内存空间用于存放位图文件
---- GlobalAlloc(GHND,FileLength);
+ q( ]+ B% V, w6 d. _
---- 2. 位图文件读入所申请内存空间中
---- LoadFileToMemory( mpBitsSrc,mFileName);
& Y( o$ p. [! s" Z
---- 3. 在OnPaint等函数中用创建显示用位图
; B2 E( ]: d g
---- 用CreateDIBitmap()创建显示用位图,用CreateCompatibleDC()创建兼容
DC,
( Z3 b5 T5 L+ G) y9 \+ ]* U: R
---- 用SelectBitmap()选择显示位图。
/ u- D. @, E! B6 t7 G
---- 4. 用BitBlt或StretchBlt等函数显示位图
- k, M q0 y" N) R0 r
---- 5. 用DeleteObject()删除所创建的位图
& E% p9 W: o# R" C* C z* a) }8 C
---- 以上方法的缺点是: 1)显示速度慢; 2) 内存占用大; 3) 位图在缩小显示
时图形失真大,(可通过安装字体平滑软件来解决); 4) 在低颜色位数的设备上(
0 R$ V* B! C: n" y如256显示模式)显示高颜色位数的图形(如真彩色)图形失真严重。
; [9 v6 u7 y! F# ?! t( F. \$ s+ C" M6 A; q" {9 S% w; i
---- 三、BMP位图缩放显示
" B2 g x. e+ c( r8 w) z" h/ G0 I% E9 M) H
---- 用DrawDib视频函数来显示位图,内存占用少,速度快,而且还可以对图形
+ X& B Q3 i( v进行淡化(Dithering)处理。淡化处理是一种图形算法,可以用来在一个支持比
图像所用颜色要少的设备上显示彩色图像。BMP位图显示方法如下:
' [+ z% [$ N- b" C8 g" H! U
---- 1. 打开视频函数DrawDibOpen(),一般放在在构造函数中
---- 2. 申请内存空间用于存放位图文件
$ G# [( L Q; T( D+ D
---- GlobalAlloc(GHND,FileLength);
( p" A! |* b8 w) ^
---- 3. 位图文件读入所申请内存空间中
: x5 I; b5 X4 Q: b# |
---- LoadFileToMemory( mpBitsSrc,mFileName);
# S5 O( e2 w* H: V: ~
---- 4. 在OnPaint等函数中用DrawDibRealize(),DrawDibDraw()显示位图
---- 5. 关闭视频函数DrawDibClose(),一般放在在析构函数中
---- 以上方法的优点是: 1)显示速度快; 2) 内存占用少; 3) 缩放显示时图形
失真小,4) 在低颜色位数的设备上显示高颜色位数的图形图形时失真小; 5) 通
: g1 e: ~) {4 {, M9 a过直接处理位图数据,可以制作简单动画。
---- 四、CViewBimap类编程要点
, q+ W: w; ]* y6 b( J: p
---- 1. 在CViewBimap类中添加视频函数等成员
HDRAWDIB m_hDrawDib; // 视频函数
HANDLEmhBitsSrc; // 位图文件句柄(内存)
LPSTR mpBitsSrc; // 位图文件地址(内存)
7 m& x: h" m% ^1 ?; `$ m- B& @, }4 ]BITMAPINFOHEADER *mpBitmapInfo; // 位图信息头
+ o- _& M) B0 v2 ~3 l. }+ J
---- 2. 在CViewBimap类构造函数中添加打开视频函数
---- m_hDrawDib= DrawDibOpen();
---- 3. 在CViewBimap类析构函数中添加关闭视频函数
9 R0 Y) a9 u! |6 f5 ~; t- c
if( m_hDrawDib != NULL)
{
) s% q' f4 E! S/ D, ^DrawDibClose( m_hDrawDib);
m_hDrawDib = NULL;
}
3 H0 a" V" c0 y+ ^1 R; b+ e8 |, s: P* j# W
5 C+ Q# v/ z* N! [3 [ a; p
---- 4. 在CViewBimap类图形显示函数OnPaint中添加GraphicDraw()
voidCViewBitmap::OnPaint()
{
0 ]7 \: P; l1 P& MCPaintDC dc(this); // device context for painting
GraphicDraw( );
, l" V) @! a3 Z; f- c}
+ F( V( r/ O C% u
voidCViewBitmap::GraphicDraw( void )
{
CClientDC dc(this); // device context for painting
BITMAPFILEHEADER *pBitmapFileHeader;
ULONG bfoffBits= 0;
7 b( X: u- h- F, NCPoint Wid;
// 图形文件名有效 (=0 BMP)
if( mBitmapFileType < ID_BITMAP_BMP ) return;
4 Y$ X9 {/ Z2 Q" ]9 L9 ?* a3 S; W! D
// 图形文件名有效 (=0 BMP)
// 准备显示真彩位图
pBitmapFileHeader= (BITMAPFILEHEADER *) mpBitsSrc;
bfoffBits= pBitmapFileHeader->bfOffBits;
& c$ x- l4 p% a* R5 `5 A% l! `# V* q" ^/ }/ o
// 使用普通函数显示位图
if( m_hDrawDib == NULL || mDispMethod == 0)
{
4 I( t+ v ?$ [3 t/ b" [4 YHBITMAP hBitmap=::CreateDIBitmap(dc.m_hDC,
' W$ d3 D) h* [4 ^ w3 nmpBitmapInfo, CBM_INIT, mpBitsSrc+bfoffBits,
(LPBITMAPINFO) mpBitmapInfo,DIB_RGB_COLORS);
) K, Y& W9 W+ b- N4 {1 U// 建立位图
HDC hMemDC=::CreateCompatibleDC(dc.m_hDC);// 建立内存
, l" M5 D8 D1 r( D: \5 AHBITMAP hBitmapOld= SelectBitmap(hMemDC, hBitmap); // 选择对象
; L; A0 T; Y# R8 u; N" B" a9 A& {// 成员CRect mDispR用于指示图形显示区域的大小.
// 成员CPoint mPos用于指示图形显示起始位置坐标.
if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))
% G6 X% J+ L& ]4 _8 SmPos.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;
if( mFullViewTog == 0)
{
$ ]+ D- [! m. I// 显示真彩位图
& _6 ^4 I* [+ `9 m::BitBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),
hMemDC,mPos.x,mPos.y, SRCCOPY);
} else {
5 g6 n- D e- j; ^" R" C! Z::StretchBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),
' |) y2 ~/ [3 I1 S z' N' @hMemDC,0,0, mpBitmapInfo- >biWidth, mpBitmapInfo-
>biHeight, SRCCOPY);
) s6 O* R; Y. ]}
8 ^) P, }; W) x5 a, v( v// 结束显示真彩位图
6 u" x9 C- e. X3 g0 N( Y :
eleteObject(SelectObject(hMemDC,hBitmapOld));
// 删 除 位 图
} else {
4 I" R0 i# `7 H* y0 T& ] b
// 使用视频函数显示位图
( s' o$ s" Y( z' h4 H9 H4 M4 k9 A9 P" D e5 i
if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))
mPos.x= mpBitmapInfo- >biWidth - mDispR.Width() ;
! ]4 M+ M9 W4 ^; N! u) Aif( mPos.y > (mpBitmapInfo- >biHeight- mDispR.Height()))
mPos.y= mpBitmapInfo- >biHeight- mDispR.Height();
7 ^, w7 \; g" w4 u, z, k( [, Iif( mPos.x < 0 ) mPos.x= 0;
7 t- D; C& P- A5 nif( mPos.y < 0 ) mPos.y= 0;
// 显示真彩位图
DrawDibRealize( m_hDrawDib, dc.GetSafeHdc(), TRUE);
if( mFullViewTog == 0)
{
Wid.x= mDispR.Width();
Wid.y= mDispR.Height();
// 1:1 显示时, 不能大于图形大小
if( Wid.x > mpBitmapInfo- >biWidth )
Wid.x = mpBitmapInfo- >biWidth;
if( Wid.y > mpBitmapInfo- >biHeight)
- W6 A# q+ I; c$ B1 c2 ]2 KWid.y = mpBitmapInfo- >biHeight;
! C# w0 ]) A8 M5 G" k
DrawDibDraw( m_hDrawDib, dc.GetSafeHdc()
& \$ {2 ~6 c9 [5 x3 h& O, B0 A, 0, 0, Wid.x, Wid.y,
mpBitmapInfo, (LPVOID) (mpBitsSrc+bfoffBits),
* l; w- r2 g! z( A( \/ f7 j. ?. QmPos.x, mPos.y, Wid.x, Wid.y, DDF_BACKGROUNDPAL);
} else {
DrawDibDraw( m_hDrawDib, dc.GetSafeHdc(),
% Z: J- e! _1 u* F0, 0, mDispR.Width(), mDispR.Height(),
mpBitmapInfo, (LPVOID) (mpBitsSrc+bfoffBits),
0, 0, mpBitmapInfo- >biWidth, mpBitmapInfo- >biHeight,
) s: C( H' S: |. i' Z) o3 NDDF_BACKGROUNDPAL);
) i: y, {8 Y) b}
}
return;
}
a+ f9 L3 b W, _5 } Y: |
/ }" y3 c& A3 }* N8 s1 C
---- 五、使用CViewBimap类显示BMP位图
---- 1. 在Visual C++5.0中新建一个名称为mymap工程文件,类型为MFC
AppWizard[exe]。在编译运行通过后,在WorkSpace(如被关闭,用Alt_0打开)点
. ?5 K7 y/ s1 a' Q; ` J1 g击ResourceView,点击Menu左侧的+符号展开Menu条目,双击IDR_MAINFRAME条目
7 v* J6 q2 ^1 H6 q,进入菜单资源编辑,在'“查看(V)”下拉式菜单(英文版为View下拉式菜单)的
尾部添加“ViewBitmap”条目,其ID为ID_VIEW_BITMAP。
4 R0 P* Z X9 t8 ?3 L" E4 W; ^# B( {; K
---- 2. 在Visual C++5.0中点击下拉式菜单Project- >Add To project-
>Files...,将Bitmap0.h和Bitmap0.cpp添加到工程文件中。
9 A7 W" ]# M& \# p
---- 3. 在Visual C++5.0中按Ctrl_W进入MFC ClassWizard,选择类名称为
4 L$ ~1 Q) z9 C: W* d2 N* wCMainFrame,ObjectIDs: ID_VIEW_BITMAP,Messages选择Command,然后点击Add
- \) C- \/ l) k7 Z' Q# \$ KFucction按钮,然后输入函数名为OnViewBimap。在添加OnViewBimap后,在
5 [* P2 i5 B5 ?$ kMember functions: 中点击OnViewBimap条目,点击Edit Code按钮编辑程序代码
2 b, `: [( I) ]- @2 j& J。代码如下:
void CMainFrame::OnViewBitmap()
7 M1 E3 n2 O* N, Y2 ]0 z% {{
& N( \8 O. z" m/ Y7 O3 R// TOD Add your command handler code here
! o4 u# c3 o ?9 |1 TCViewBitmap *pViewBitmap= NULL;
7 N2 O9 k$ K+ ]$ H3 e& {
pViewBitmap= new CViewBitmap( "BITMAP.BMP", this);
+ O7 y, E) t( p: i m! h0 qpViewBitmap- >ShowWindow( TRUE);
}
4 m' C) ~' C9 N; S+ y, p! ?4 ?4 s. c, k% b6 _
5 U0 o/ J7 K1 o0 J3 v* G
---- 并在该程序的头部添加#include "bitmap0.h",然后编译运行。
---- 4. 找一个大一点的真彩色的BMP位图,将它拷贝到BITMAP.BMP中。
r5 L; a1 T% n- P) H- c1 e1 }3 U2 U" D P0 S; ~# H
---- 5. 运行时,点击下拉式菜单“查看(V)- >ViewBitmap”(英文版为View- >
ViewBitmap)即可显示BITMAP.BMP位图。
; d& e* \. ~4 z, C' E! M7 `
---- 六、CViewBimap类功能说明
$ T6 Y: _' A- M9 D1 w8 m
---- 1. 在客户区中带有水平和垂直滚动条。在位图大小大于显示客户区时,可
Z8 u; R0 z1 B3 m9 p以使用滚动条;在位图大小小于显示客户区或全屏显示时,滚动条无效。
% B: K* e/ Q- _# X6 M, C& ]
---- 2. 在客户区中底部带有状态条。状态条中的第一格为位图信息,第二格为
2 I) P- y! l; ^2 M位图显示方法,可以是使用普通函数或使用视频函数。在第二格区域内点击鼠标
# D( _% p% ^% A3 },可在两者之间接换。第三格为位图显示比例,可以是1;1显示或全屏显示。在
第三格区域内点击鼠标,可在两者之间接换。在全屏显示时,如果位图比客户区
小,则对位图放大; 如果位图比客户区大,则对位图缩小。
---- 3. 支持文件拖放功能。可以从资源管理器中拖动一个位图文件到客户区,
就可以显示该位图。
2 Q' |% T2 A4 b7 r
---- 程序调试通过后,可以找一个较大的真彩色位图或调整客户区比位图小,
在全屏显示方式下,比较使用普通函数与使用视频函数的差别。可以看出,位图
5 f/ j, S& W' C; V1 p放大时两者差别不大,但在位图缩小时,两者差别明显; 使用视频函数时位图失
& b0 b8 I; ]/ ?/ i* ~: {6 S |真小,显示速度快。
4 b* `. W. F! l, j& j; H5 x6 l: _7 ^; \" b6 V' q _' L
---- 还可以从控制面板中将屏幕显示方式从真彩色显示模式切换到256色显示模
式,再比较使用普通函数与使用视频函数显示同一个真彩色位图的差别。现在可
以体会到使用视频函数的优越性了吧。
# k, w& `. D2 H" q
---- 在全屏显示时,位图的xy方向比例不相同,如要保持相同比例,可在显示
程序中加以适当调整即可,读者可自行完成。
| 欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) | Powered by Discuz! X2.5 |